You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Colm MacCarthaigh <co...@stdlib.net> on 2006/01/24 22:02:40 UTC

Documentation for mod_cgid suexec patch

I'm conscious that cgid not many people deal with the cgid code, to the
point that before the Autumn, I think the last major change to it was a
patch I submitted 4 years ago, so it's not fresh in people's minds. 

Nevertheless I'm also conscious that it's used by a lot of people, and
some of them want to use it with third party modules that implement the
suexec hook, and also that the httpd API should really work as
advertised.

So, in a last effort to get the patch into 2.0.x, here's some verbose
documentation on what it does :) If the patch doesn't get in, I suggest
we declare the API broken in 2.0.x and just leave it at that. It's fixed
in 2.2.x and we can point people there.

The patch I'm talking about is:

	http://people.apache.org/~colm/2.0.x-suexec-cgid.patch

Background:

	mod_cgid forks off a daemon process at startup. I'm going to 
	refer to this process as "cgid", distinct from the "httpd"
	side of the code. Before the fork a unix socket is created
	and both the httpd side and the cgid maintain a reference
	to this.

	When mod_cgid handles a request - within httpd - it presently
	works out relevant metadata concerning the process to be
	executed and writes that accross to cgid, which forks and
	execs the neccessary process.

The problem:

	Right now, part of the metadata that we send to cgid is the
	"mod_userdir_user" note from r->notes. So that when the
	mod_userdir suexec identity hook is run on the cgid side
	it doesn't segfault, and we get the right user to run the
	process as.

	In essence, this means we have a mod_userdir-specific hack
	in cgid to make the suexec identity hook work. When other
	modules implement this hook, and they get run within cgid
	they don't have their usual environment available. r->notes
	is meaningless to them, other places they search may be
	unavailable, and so on. So the hook simply doesn't work.

The solution:

	Instead of just running the hook within cgid, the patch
	changes is so that the hook is run within httpd, where all
	of the environment is available, r->notes is set-up properly
	and so on.

The details:

	On the httpd side, after we've run the hook, where we used
	to send the userdir note from r->notes, now we send the
	ap_unix_identity_t structure. If the resulf of running the
	hook was NULL (ie don't use suexec), then we send over
	a magic instance of the structure, corresponding to:

	  static ap_unix_identity_t empty_ugid = {(uid_t)-1, (gid_t)-1, -1 };

	Although -1 is a possible (though unlikely) value for the first
	two elements. -1 is not a valid element for the third element
	(int suexec_enabled; see os/unix/unixd.h) so there is no
	danger of confusing a valid suexec identity structure with our
	magic constants.

	Patched, cgid has it's own suexec identity doer. It's
	cgid_suexec_id_doer(). It's a simple doer, and just assumes that
	the entire request config is the ap_unix_identity_t structure.
	When cgid starts we add this doer with the REALLY_FIRST
	instruction, and then do an explicit apr_hook_sort_all(); This
	guarantees that - within cgid - our own private doer will be run
	first amongst the registered doers for the hook.

	When cgid receives its metadata, it blindly does:

	ap_set_module_config(r->request_config, &cgid_module, (void *)&req->ugid);

	At this point that ugid may be a valid one, or it may be
	identical to the empty_ugid magic constant. However the request
	config is now set-up, and is something our own private doer can
	return. 

	Lastly, to ensure that our doer is never called when the
	identity is really empty; cgid now checks to see if the ugid
	is the empty one, if so it will not call
	ap_os_create_privileged_process() (which runs the hook), and
	instead calls regular apr_proc_create() (which does not run
	the hook).

	The design decision on that last point was this: it may 
	appear tempting to instead have to doer do something like:

		if(!memcmp(ugid, empty_ugid, sizeof(ugid)))
		    return NULL;

	but if our doer ever return'd null, we'd have a problem
	as the next doer registered with the hook would be run. And
	we'd back to the original problem. Although I guess an
	alternative would be to use apr_hook_deregister_all() and
	only register our own hook.

That's it, the patch should be reasnoably clear. If anyone does get a
chance or have an inclination, it's appreciated. If not, we can mark the
API as broken.

-- 
Colm MacCárthaigh                        Public Key: colm+pgp@stdlib.net