use the real uid to do the setuid_root (this
[mod_fastcgi.git] / fcgi_util.c
blob9804e04a33c98fb0fc9e7fdb5b190e4c867b1ff7
1 /*
2 * $Id: fcgi_util.c,v 1.28 2002/10/22 01:02:18 robs Exp $
3 */
5 #include "fcgi.h"
7 #ifdef WIN32
8 #pragma warning( disable : 4100 )
9 #elif defined(APACHE2)
10 #include <netdb.h>
11 #include <unistd.h>
12 #include <grp.h>
13 #include <pwd.h>
15 #if APR_HAVE_ARPA_INET_H
16 #include <arpa/inet.h>
17 #endif
19 #include "unixd.h"
20 #endif
22 uid_t
23 fcgi_util_get_server_uid(const server_rec * const s)
25 #ifdef APACHE2
26 /* the main server's uid */
27 return ap_user_id;
28 #else
29 /* the vhost's uid */
30 return s->server_uid;
31 #endif
34 uid_t
35 fcgi_util_get_server_gid(const server_rec * const s)
37 #ifdef APACHE2
38 /* the main server's gid */
39 return ap_group_id;
40 #else
41 /* the vhost's gid */
42 return s->server_gid;
43 #endif
46 /*******************************************************************************
47 * Compute printable MD5 hash. Pool p is used for scratch as well as for
48 * allocating the hash - use temp storage, and dup it if you need to keep it.
50 char *
51 fcgi_util_socket_hash_filename(pool *p, const char *path,
52 const char *user, const char *group)
54 char *buf = ap_pstrcat(p, path, user, group, NULL);
56 /* Canonicalize the path (remove "//", ".", "..") */
57 ap_getparents(buf);
59 return ap_md5(p, (unsigned char *)buf);
63 /*******************************************************************************
64 * Concat src1 and src2 using the approprate path seperator for the platform.
66 static char * make_full_path(pool *a, const char *src1, const char *src2)
68 #ifdef WIN32
69 register int x;
70 char * p ;
71 char * q ;
73 x = strlen(src1);
75 if (x == 0) {
76 p = ap_pstrcat(a, "\\", src2, NULL);
78 else if (src1[x - 1] != '\\' && src1[x - 1] != '/') {
79 p = ap_pstrcat(a, src1, "\\", src2, NULL);
81 else {
82 p = ap_pstrcat(a, src1, src2, NULL);
85 q = p ;
86 while (*q)
88 if (*q == '/') {
89 *q = '\\' ;
91 ++q;
94 return p ;
95 #else
96 return ap_make_full_path(a, src1, src2);
97 #endif
100 /*******************************************************************************
101 * Return absolute path to file in either "regular" FCGI socket directory or
102 * the dynamic directory. Result is allocated in pool p.
104 const char *
105 fcgi_util_socket_make_path_absolute(pool * const p,
106 const char *const file, const int dynamic)
108 #ifdef APACHE2
109 if (ap_os_is_path_absolute(p, (char *) file))
110 #else
111 if (ap_os_is_path_absolute(file))
112 #endif
114 return file;
116 else
118 const char * parent_dir = dynamic ? fcgi_dynamic_dir : fcgi_socket_dir;
119 return (const char *) make_full_path(p, parent_dir, file);
123 #ifndef WIN32
124 /*******************************************************************************
125 * Build a Domain Socket Address structure, and calculate its size.
126 * The error message is allocated from the pool p. If you don't want the
127 * struct sockaddr_un also allocated from p, pass it preallocated (!=NULL).
129 const char *
130 fcgi_util_socket_make_domain_addr(pool *p, struct sockaddr_un **socket_addr,
131 int *socket_addr_len, const char *socket_path)
133 int socket_pathLen = strlen(socket_path);
135 if (socket_pathLen >= sizeof((*socket_addr)->sun_path)) {
136 return ap_pstrcat(p, "path \"", socket_path,
137 "\" is too long for a Domain socket", NULL);
140 if (*socket_addr == NULL)
141 *socket_addr = ap_pcalloc(p, sizeof(struct sockaddr_un));
142 else
143 memset(*socket_addr, 0, sizeof(struct sockaddr_un));
145 (*socket_addr)->sun_family = AF_UNIX;
146 strcpy((*socket_addr)->sun_path, socket_path);
148 *socket_addr_len = SUN_LEN(*socket_addr);
149 return NULL;
151 #endif
153 /*******************************************************************************
154 * Convert a hostname or IP address string to an in_addr struct.
156 static int
157 convert_string_to_in_addr(const char * const hostname, struct in_addr * const addr)
159 struct hostent *hp;
160 int count;
162 addr->s_addr = inet_addr((char *)hostname);
164 #if !defined(INADDR_NONE) && defined(APACHE2)
165 #define INADDR_NONE APR_INADDR_NONE
166 #endif
168 if (addr->s_addr == INADDR_NONE) {
169 if ((hp = gethostbyname((char *)hostname)) == NULL)
170 return -1;
172 memcpy((char *) addr, hp->h_addr, hp->h_length);
173 count = 0;
174 while (hp->h_addr_list[count] != 0)
175 count++;
177 return count;
179 return 1;
183 /*******************************************************************************
184 * Build an Inet Socket Address structure, and calculate its size.
185 * The error message is allocated from the pool p. If you don't want the
186 * struct sockaddr_in also allocated from p, pass it preallocated (!=NULL).
188 const char *
189 fcgi_util_socket_make_inet_addr(pool *p, struct sockaddr_in **socket_addr,
190 int *socket_addr_len, const char *host, unsigned short port)
192 if (*socket_addr == NULL)
193 *socket_addr = ap_pcalloc(p, sizeof(struct sockaddr_in));
194 else
195 memset(*socket_addr, 0, sizeof(struct sockaddr_in));
197 (*socket_addr)->sin_family = AF_INET;
198 (*socket_addr)->sin_port = htons(port);
200 /* Get an in_addr represention of the host */
201 if (host != NULL) {
202 if (convert_string_to_in_addr(host, &(*socket_addr)->sin_addr) != 1) {
203 return ap_pstrcat(p, "failed to resolve \"", host,
204 "\" to exactly one IP address", NULL);
206 } else {
207 (*socket_addr)->sin_addr.s_addr = htonl(INADDR_ANY);
210 *socket_addr_len = sizeof(struct sockaddr_in);
211 return NULL;
214 /*******************************************************************************
215 * Determine if a process with uid/gid can access a file with mode permissions.
217 const char *
218 fcgi_util_check_access(pool *tp,
219 const char * const path, const struct stat *statBuf,
220 const int mode, const uid_t uid, const gid_t gid)
222 struct stat myStatBuf;
224 if (statBuf == NULL) {
225 if (stat(path, &myStatBuf) < 0)
226 return ap_psprintf(tp, "stat(%s) failed: %s", path, strerror(errno));
227 statBuf = &myStatBuf;
230 #ifndef WIN32
231 /* If the uid owns the file, check the owner bits */
232 if (uid == statBuf->st_uid) {
233 if (mode & R_OK && !(statBuf->st_mode & S_IRUSR))
234 return "read not allowed by owner";
235 if (mode & W_OK && !(statBuf->st_mode & S_IWUSR))
236 return "write not allowed by owner";
237 if (mode & X_OK && !(statBuf->st_mode & S_IXUSR))
238 return "execute not allowed by owner";
239 return NULL;
241 #else
242 if (mode & _S_IREAD && !(statBuf->st_mode & _S_IREAD))
243 return "read not allowed";
244 if (mode & _S_IWRITE && !(statBuf->st_mode & _S_IWRITE))
245 return "write not allowed";
247 // I don't think this works on FAT, but since I don't know how to check..
248 // if (mode & _S_IEXEC && !(statBuf->st_mode & _S_IEXEC))
249 // return "execute not allowed";
250 #endif
252 #if !defined(__EMX__) && !defined(WIN32)
253 /* If the gid is same as the file's group, check the group bits */
254 if (gid == statBuf->st_gid) {
255 if (mode & R_OK && !(statBuf->st_mode & S_IRGRP))
256 return "read not allowed by group";
257 if (mode & W_OK && !(statBuf->st_mode & S_IWGRP))
258 return "write not allowed by group";
259 if (mode & X_OK && !(statBuf->st_mode & S_IXGRP))
260 return "execute not allowed by group";
261 return NULL;
264 /* Get the user membership for the file's group. If the
265 * uid is a member, check the group bits. */
267 const struct group * const gr = getgrgid(statBuf->st_gid);
268 const struct passwd * const pw = getpwuid(uid);
270 if (gr != NULL && pw != NULL) {
271 char **user = gr->gr_mem;
272 for ( ; *user != NULL; user++) {
273 if (strcmp(*user, pw->pw_name) == 0) {
274 if (mode & R_OK && !(statBuf->st_mode & S_IRGRP))
275 return "read not allowed by group";
276 if (mode & W_OK && !(statBuf->st_mode & S_IWGRP))
277 return "write not allowed by group";
278 if (mode & X_OK && !(statBuf->st_mode & S_IXGRP))
279 return "execute not allowed by group";
280 return NULL;
286 /* That just leaves the other bits.. */
287 if (mode & R_OK && !(statBuf->st_mode & S_IROTH))
288 return "read not allowed";
289 if (mode & W_OK && !(statBuf->st_mode & S_IWOTH))
290 return "write not allowed";
291 if (mode & X_OK && !(statBuf->st_mode & S_IXOTH))
292 return "execute not allowed";
293 #endif
295 return NULL;
299 /*******************************************************************************
300 * Find a FastCGI server with a matching fs_path, and if fcgi_wrapper is
301 * enabled with matching uid and gid.
303 fcgi_server *
304 fcgi_util_fs_get_by_id(const char *ePath, uid_t uid, gid_t gid)
306 char path[FCGI_MAXPATH];
307 fcgi_server *s;
309 /* @@@ This should now be done in the loop below */
310 ap_cpystrn(path, ePath, FCGI_MAXPATH);
311 ap_no2slash(path);
313 for (s = fcgi_servers; s != NULL; s = s->next) {
314 int i;
315 const char *fs_path = s->fs_path;
316 for (i = 0; fs_path[i] && path[i]; ++i) {
317 if (fs_path[i] != path[i]) {
318 break;
321 if (fs_path[i]) {
322 continue;
324 if (path[i] == '\0' || path[i] == '/') {
325 if (fcgi_wrapper == NULL || (uid == s->uid && gid == s->gid))
326 return s;
329 return NULL;
332 /*******************************************************************************
333 * Find a FastCGI server with a matching fs_path, and if fcgi_wrapper is
334 * enabled with matching user and group.
336 fcgi_server *
337 fcgi_util_fs_get(const char *ePath, const char *user, const char *group)
339 char path[FCGI_MAXPATH];
340 fcgi_server *s;
342 ap_cpystrn(path, ePath, FCGI_MAXPATH);
343 ap_no2slash(path);
345 for (s = fcgi_servers; s != NULL; s = s->next) {
346 if (strcmp(s->fs_path, path) == 0) {
347 if (fcgi_wrapper == NULL)
348 return s;
350 if (strcmp(user, s->user) == 0
351 && (user[0] == '~' || strcmp(group, s->group) == 0))
353 return s;
357 return NULL;
360 const char *
361 fcgi_util_fs_is_path_ok(pool * const p, const char * const fs_path, struct stat *finfo)
363 const char *err;
365 if (finfo == NULL) {
366 finfo = (struct stat *)ap_palloc(p, sizeof(struct stat));
367 if (stat(fs_path, finfo) < 0)
368 return ap_psprintf(p, "stat(%s) failed: %s", fs_path, strerror(errno));
371 /* No Parse Header scripts aren't allowed.
372 * @@@ Well... we really could quite easily */
373 if (strncmp(strrchr(fs_path, '/'), "/nph-", 5) == 0)
374 return ap_psprintf(p, "NPH scripts cannot be run as FastCGI");
376 if (finfo->st_mode == 0)
377 return ap_psprintf(p, "script not found or unable to stat()");
379 if (S_ISDIR(finfo->st_mode))
380 return ap_psprintf(p, "script is a directory!");
382 /* Let the wrapper determine what it can and can't execute */
383 if (! fcgi_wrapper)
385 #ifdef WIN32
386 err = fcgi_util_check_access(p, fs_path, finfo, _S_IEXEC, fcgi_user_id, fcgi_group_id);
387 #else
388 err = fcgi_util_check_access(p, fs_path, finfo, X_OK, fcgi_user_id, fcgi_group_id);
389 #endif
390 if (err) {
391 return ap_psprintf(p,
392 "access for server (uid %ld, gid %ld) not allowed: %s",
393 (long)fcgi_user_id, (long)fcgi_group_id, err);
397 return NULL;
402 /*******************************************************************************
403 * Allocate a new FastCGI server record from pool p with default values.
405 fcgi_server *
406 fcgi_util_fs_new(pool *p)
408 fcgi_server *s = (fcgi_server *) ap_pcalloc(p, sizeof(fcgi_server));
410 /* Initialize anything who's init state is not zeroizzzzed */
411 s->listenQueueDepth = FCGI_DEFAULT_LISTEN_Q;
412 s->appConnectTimeout = FCGI_DEFAULT_APP_CONN_TIMEOUT;
413 s->idle_timeout = FCGI_DEFAULT_IDLE_TIMEOUT;
414 s->initStartDelay = DEFAULT_INIT_START_DELAY;
415 s->restartDelay = FCGI_DEFAULT_RESTART_DELAY;
416 s->restartOnExit = FALSE;
417 s->directive = APP_CLASS_UNKNOWN;
418 s->processPriority = FCGI_DEFAULT_PRIORITY;
419 s->envp = &fcgi_empty_env;
421 #ifdef WIN32
422 s->listenFd = (int) INVALID_HANDLE_VALUE;
423 #else
424 s->listenFd = -2;
425 #endif
427 return s;
430 /*******************************************************************************
431 * Add the server to the linked list of FastCGI servers.
433 void
434 fcgi_util_fs_add(fcgi_server *s)
436 s->next = fcgi_servers;
437 fcgi_servers = s;
440 /*******************************************************************************
441 * Configure uid, gid, user, group, username for wrapper.
443 const char *
444 fcgi_util_fs_set_uid_n_gid(pool *p, fcgi_server *s, uid_t uid, gid_t gid)
446 #ifndef WIN32
448 struct passwd *pw;
449 struct group *gr;
451 if (fcgi_wrapper == NULL)
452 return NULL;
454 if (uid == 0 || gid == 0) {
455 return "invalid uid or gid, see the -user and -group options";
458 s->uid = uid;
459 pw = getpwuid(uid);
460 if (pw == NULL) {
461 return ap_psprintf(p,
462 "getpwuid() couldn't determine the username for uid '%ld', "
463 "you probably need to modify the User directive: %s",
464 (long)uid, strerror(errno));
466 s->user = ap_pstrdup(p, pw->pw_name);
467 s->username = s->user;
469 s->gid = gid;
470 gr = getgrgid(gid);
471 if (gr == NULL) {
472 return ap_psprintf(p,
473 "getgrgid() couldn't determine the group name for gid '%ld', "
474 "you probably need to modify the Group directive: %s",
475 (long)gid, strerror(errno));
477 s->group = ap_pstrdup(p, gr->gr_name);
479 #endif /* !WIN32 */
481 return NULL;
484 /*******************************************************************************
485 * Allocate an array of ServerProcess records.
487 ServerProcess *
488 fcgi_util_fs_create_procs(pool *p, int num)
490 int i;
491 ServerProcess *proc = (ServerProcess *)ap_pcalloc(p, sizeof(ServerProcess) * num);
493 for (i = 0; i < num; i++) {
494 #ifdef WIN32
495 proc[i].handle = INVALID_HANDLE_VALUE;
496 proc[i].terminationEvent = INVALID_HANDLE_VALUE;
497 #endif
498 proc[i].pid = 0;
499 proc[i].state = FCGI_READY_STATE;
501 return proc;
504 int fcgi_util_ticks(struct timeval * tv)
506 #ifdef WIN32
507 // millisecs is sufficent granularity
508 DWORD millis = GetTickCount();
510 tv->tv_sec = millis / 1000;
511 tv->tv_usec = (millis % 1000) * 1000;
513 return 0;
514 #else
515 return gettimeofday(tv, NULL);
516 #endif