check for the presence of the wrapper when 'On' is used, default the user/group for...
[mod_fastcgi.git] / fcgi_util.c
blob5455182ad26df00cdebce5943095da836a6123ee
1 /*
2 * $Id: fcgi_util.c,v 1.27 2002/10/21 23:54:24 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>
14 #include "unixd.h"
15 #endif
17 uid_t
18 fcgi_util_get_server_uid(const server_rec * const s)
20 #ifdef APACHE2
21 /* the main server's uid */
22 return ap_user_id;
23 #else
24 /* the vhost's uid */
25 return s->server_uid;
26 #endif
29 uid_t
30 fcgi_util_get_server_gid(const server_rec * const s)
32 #ifdef APACHE2
33 /* the main server's gid */
34 return ap_group_id;
35 #else
36 /* the vhost's gid */
37 return s->server_gid;
38 #endif
41 /*******************************************************************************
42 * Compute printable MD5 hash. Pool p is used for scratch as well as for
43 * allocating the hash - use temp storage, and dup it if you need to keep it.
45 char *
46 fcgi_util_socket_hash_filename(pool *p, const char *path,
47 const char *user, const char *group)
49 char *buf = ap_pstrcat(p, path, user, group, NULL);
51 /* Canonicalize the path (remove "//", ".", "..") */
52 ap_getparents(buf);
54 return ap_md5(p, (unsigned char *)buf);
58 /*******************************************************************************
59 * Concat src1 and src2 using the approprate path seperator for the platform.
61 static char * make_full_path(pool *a, const char *src1, const char *src2)
63 #ifdef WIN32
64 register int x;
65 char * p ;
66 char * q ;
68 x = strlen(src1);
70 if (x == 0) {
71 p = ap_pstrcat(a, "\\", src2, NULL);
73 else if (src1[x - 1] != '\\' && src1[x - 1] != '/') {
74 p = ap_pstrcat(a, src1, "\\", src2, NULL);
76 else {
77 p = ap_pstrcat(a, src1, src2, NULL);
80 q = p ;
81 while (*q)
83 if (*q == '/') {
84 *q = '\\' ;
86 ++q;
89 return p ;
90 #else
91 return ap_make_full_path(a, src1, src2);
92 #endif
95 /*******************************************************************************
96 * Return absolute path to file in either "regular" FCGI socket directory or
97 * the dynamic directory. Result is allocated in pool p.
99 const char *
100 fcgi_util_socket_make_path_absolute(pool * const p,
101 const char *const file, const int dynamic)
103 #ifdef APACHE2
104 if (ap_os_is_path_absolute(p, (char *) file))
105 #else
106 if (ap_os_is_path_absolute(file))
107 #endif
109 return file;
111 else
113 const char * parent_dir = dynamic ? fcgi_dynamic_dir : fcgi_socket_dir;
114 return (const char *) make_full_path(p, parent_dir, file);
118 #ifndef WIN32
119 /*******************************************************************************
120 * Build a Domain Socket Address structure, and calculate its size.
121 * The error message is allocated from the pool p. If you don't want the
122 * struct sockaddr_un also allocated from p, pass it preallocated (!=NULL).
124 const char *
125 fcgi_util_socket_make_domain_addr(pool *p, struct sockaddr_un **socket_addr,
126 int *socket_addr_len, const char *socket_path)
128 int socket_pathLen = strlen(socket_path);
130 if (socket_pathLen >= sizeof((*socket_addr)->sun_path)) {
131 return ap_pstrcat(p, "path \"", socket_path,
132 "\" is too long for a Domain socket", NULL);
135 if (*socket_addr == NULL)
136 *socket_addr = ap_pcalloc(p, sizeof(struct sockaddr_un));
137 else
138 memset(*socket_addr, 0, sizeof(struct sockaddr_un));
140 (*socket_addr)->sun_family = AF_UNIX;
141 strcpy((*socket_addr)->sun_path, socket_path);
143 *socket_addr_len = SUN_LEN(*socket_addr);
144 return NULL;
146 #endif
148 /*******************************************************************************
149 * Convert a hostname or IP address string to an in_addr struct.
151 static int
152 convert_string_to_in_addr(const char * const hostname, struct in_addr * const addr)
154 struct hostent *hp;
155 int count;
157 addr->s_addr = inet_addr((char *)hostname);
159 #if !defined(INADDR_NONE) && defined(APACHE2)
160 #define INADDR_NONE APR_INADDR_NONE
161 #endif
163 if (addr->s_addr == INADDR_NONE) {
164 if ((hp = gethostbyname((char *)hostname)) == NULL)
165 return -1;
167 memcpy((char *) addr, hp->h_addr, hp->h_length);
168 count = 0;
169 while (hp->h_addr_list[count] != 0)
170 count++;
172 return count;
174 return 1;
178 /*******************************************************************************
179 * Build an Inet Socket Address structure, and calculate its size.
180 * The error message is allocated from the pool p. If you don't want the
181 * struct sockaddr_in also allocated from p, pass it preallocated (!=NULL).
183 const char *
184 fcgi_util_socket_make_inet_addr(pool *p, struct sockaddr_in **socket_addr,
185 int *socket_addr_len, const char *host, unsigned short port)
187 if (*socket_addr == NULL)
188 *socket_addr = ap_pcalloc(p, sizeof(struct sockaddr_in));
189 else
190 memset(*socket_addr, 0, sizeof(struct sockaddr_in));
192 (*socket_addr)->sin_family = AF_INET;
193 (*socket_addr)->sin_port = htons(port);
195 /* Get an in_addr represention of the host */
196 if (host != NULL) {
197 if (convert_string_to_in_addr(host, &(*socket_addr)->sin_addr) != 1) {
198 return ap_pstrcat(p, "failed to resolve \"", host,
199 "\" to exactly one IP address", NULL);
201 } else {
202 (*socket_addr)->sin_addr.s_addr = htonl(INADDR_ANY);
205 *socket_addr_len = sizeof(struct sockaddr_in);
206 return NULL;
209 /*******************************************************************************
210 * Determine if a process with uid/gid can access a file with mode permissions.
212 const char *
213 fcgi_util_check_access(pool *tp,
214 const char * const path, const struct stat *statBuf,
215 const int mode, const uid_t uid, const gid_t gid)
217 struct stat myStatBuf;
219 if (statBuf == NULL) {
220 if (stat(path, &myStatBuf) < 0)
221 return ap_psprintf(tp, "stat(%s) failed: %s", path, strerror(errno));
222 statBuf = &myStatBuf;
225 #ifndef WIN32
226 /* If the uid owns the file, check the owner bits */
227 if (uid == statBuf->st_uid) {
228 if (mode & R_OK && !(statBuf->st_mode & S_IRUSR))
229 return "read not allowed by owner";
230 if (mode & W_OK && !(statBuf->st_mode & S_IWUSR))
231 return "write not allowed by owner";
232 if (mode & X_OK && !(statBuf->st_mode & S_IXUSR))
233 return "execute not allowed by owner";
234 return NULL;
236 #else
237 if (mode & _S_IREAD && !(statBuf->st_mode & _S_IREAD))
238 return "read not allowed";
239 if (mode & _S_IWRITE && !(statBuf->st_mode & _S_IWRITE))
240 return "write not allowed";
242 // I don't think this works on FAT, but since I don't know how to check..
243 // if (mode & _S_IEXEC && !(statBuf->st_mode & _S_IEXEC))
244 // return "execute not allowed";
245 #endif
247 #if !defined(__EMX__) && !defined(WIN32)
248 /* If the gid is same as the file's group, check the group bits */
249 if (gid == statBuf->st_gid) {
250 if (mode & R_OK && !(statBuf->st_mode & S_IRGRP))
251 return "read not allowed by group";
252 if (mode & W_OK && !(statBuf->st_mode & S_IWGRP))
253 return "write not allowed by group";
254 if (mode & X_OK && !(statBuf->st_mode & S_IXGRP))
255 return "execute not allowed by group";
256 return NULL;
259 /* Get the user membership for the file's group. If the
260 * uid is a member, check the group bits. */
262 const struct group * const gr = getgrgid(statBuf->st_gid);
263 const struct passwd * const pw = getpwuid(uid);
265 if (gr != NULL && pw != NULL) {
266 char **user = gr->gr_mem;
267 for ( ; *user != NULL; user++) {
268 if (strcmp(*user, pw->pw_name) == 0) {
269 if (mode & R_OK && !(statBuf->st_mode & S_IRGRP))
270 return "read not allowed by group";
271 if (mode & W_OK && !(statBuf->st_mode & S_IWGRP))
272 return "write not allowed by group";
273 if (mode & X_OK && !(statBuf->st_mode & S_IXGRP))
274 return "execute not allowed by group";
275 return NULL;
281 /* That just leaves the other bits.. */
282 if (mode & R_OK && !(statBuf->st_mode & S_IROTH))
283 return "read not allowed";
284 if (mode & W_OK && !(statBuf->st_mode & S_IWOTH))
285 return "write not allowed";
286 if (mode & X_OK && !(statBuf->st_mode & S_IXOTH))
287 return "execute not allowed";
288 #endif
290 return NULL;
294 /*******************************************************************************
295 * Find a FastCGI server with a matching fs_path, and if fcgi_wrapper is
296 * enabled with matching uid and gid.
298 fcgi_server *
299 fcgi_util_fs_get_by_id(const char *ePath, uid_t uid, gid_t gid)
301 char path[FCGI_MAXPATH];
302 fcgi_server *s;
304 /* @@@ This should now be done in the loop below */
305 ap_cpystrn(path, ePath, FCGI_MAXPATH);
306 ap_no2slash(path);
308 for (s = fcgi_servers; s != NULL; s = s->next) {
309 int i;
310 const char *fs_path = s->fs_path;
311 for (i = 0; fs_path[i] && path[i]; ++i) {
312 if (fs_path[i] != path[i]) {
313 break;
316 if (fs_path[i]) {
317 continue;
319 if (path[i] == '\0' || path[i] == '/') {
320 if (fcgi_wrapper == NULL || (uid == s->uid && gid == s->gid))
321 return s;
324 return NULL;
327 /*******************************************************************************
328 * Find a FastCGI server with a matching fs_path, and if fcgi_wrapper is
329 * enabled with matching user and group.
331 fcgi_server *
332 fcgi_util_fs_get(const char *ePath, const char *user, const char *group)
334 char path[FCGI_MAXPATH];
335 fcgi_server *s;
337 ap_cpystrn(path, ePath, FCGI_MAXPATH);
338 ap_no2slash(path);
340 for (s = fcgi_servers; s != NULL; s = s->next) {
341 if (strcmp(s->fs_path, path) == 0) {
342 if (fcgi_wrapper == NULL)
343 return s;
345 if (strcmp(user, s->user) == 0
346 && (user[0] == '~' || strcmp(group, s->group) == 0))
348 return s;
352 return NULL;
355 const char *
356 fcgi_util_fs_is_path_ok(pool * const p, const char * const fs_path, struct stat *finfo)
358 const char *err;
360 if (finfo == NULL) {
361 finfo = (struct stat *)ap_palloc(p, sizeof(struct stat));
362 if (stat(fs_path, finfo) < 0)
363 return ap_psprintf(p, "stat(%s) failed: %s", fs_path, strerror(errno));
366 /* No Parse Header scripts aren't allowed.
367 * @@@ Well... we really could quite easily */
368 if (strncmp(strrchr(fs_path, '/'), "/nph-", 5) == 0)
369 return ap_psprintf(p, "NPH scripts cannot be run as FastCGI");
371 if (finfo->st_mode == 0)
372 return ap_psprintf(p, "script not found or unable to stat()");
374 if (S_ISDIR(finfo->st_mode))
375 return ap_psprintf(p, "script is a directory!");
377 /* Let the wrapper determine what it can and can't execute */
378 if (! fcgi_wrapper)
380 #ifdef WIN32
381 err = fcgi_util_check_access(p, fs_path, finfo, _S_IEXEC, fcgi_user_id, fcgi_group_id);
382 #else
383 err = fcgi_util_check_access(p, fs_path, finfo, X_OK, fcgi_user_id, fcgi_group_id);
384 #endif
385 if (err) {
386 return ap_psprintf(p,
387 "access for server (uid %ld, gid %ld) not allowed: %s",
388 (long)fcgi_user_id, (long)fcgi_group_id, err);
392 return NULL;
397 /*******************************************************************************
398 * Allocate a new FastCGI server record from pool p with default values.
400 fcgi_server *
401 fcgi_util_fs_new(pool *p)
403 fcgi_server *s = (fcgi_server *) ap_pcalloc(p, sizeof(fcgi_server));
405 /* Initialize anything who's init state is not zeroizzzzed */
406 s->listenQueueDepth = FCGI_DEFAULT_LISTEN_Q;
407 s->appConnectTimeout = FCGI_DEFAULT_APP_CONN_TIMEOUT;
408 s->idle_timeout = FCGI_DEFAULT_IDLE_TIMEOUT;
409 s->initStartDelay = DEFAULT_INIT_START_DELAY;
410 s->restartDelay = FCGI_DEFAULT_RESTART_DELAY;
411 s->restartOnExit = FALSE;
412 s->directive = APP_CLASS_UNKNOWN;
413 s->processPriority = FCGI_DEFAULT_PRIORITY;
414 s->envp = &fcgi_empty_env;
416 #ifdef WIN32
417 s->listenFd = (int) INVALID_HANDLE_VALUE;
418 #else
419 s->listenFd = -2;
420 #endif
422 return s;
425 /*******************************************************************************
426 * Add the server to the linked list of FastCGI servers.
428 void
429 fcgi_util_fs_add(fcgi_server *s)
431 s->next = fcgi_servers;
432 fcgi_servers = s;
435 /*******************************************************************************
436 * Configure uid, gid, user, group, username for wrapper.
438 const char *
439 fcgi_util_fs_set_uid_n_gid(pool *p, fcgi_server *s, uid_t uid, gid_t gid)
441 #ifndef WIN32
443 struct passwd *pw;
444 struct group *gr;
446 if (fcgi_wrapper == NULL)
447 return NULL;
449 if (uid == 0 || gid == 0) {
450 return "invalid uid or gid, see the -user and -group options";
453 s->uid = uid;
454 pw = getpwuid(uid);
455 if (pw == NULL) {
456 return ap_psprintf(p,
457 "getpwuid() couldn't determine the username for uid '%ld', "
458 "you probably need to modify the User directive: %s",
459 (long)uid, strerror(errno));
461 s->user = ap_pstrdup(p, pw->pw_name);
462 s->username = s->user;
464 s->gid = gid;
465 gr = getgrgid(gid);
466 if (gr == NULL) {
467 return ap_psprintf(p,
468 "getgrgid() couldn't determine the group name for gid '%ld', "
469 "you probably need to modify the Group directive: %s",
470 (long)gid, strerror(errno));
472 s->group = ap_pstrdup(p, gr->gr_name);
474 #endif /* !WIN32 */
476 return NULL;
479 /*******************************************************************************
480 * Allocate an array of ServerProcess records.
482 ServerProcess *
483 fcgi_util_fs_create_procs(pool *p, int num)
485 int i;
486 ServerProcess *proc = (ServerProcess *)ap_pcalloc(p, sizeof(ServerProcess) * num);
488 for (i = 0; i < num; i++) {
489 #ifdef WIN32
490 proc[i].handle = INVALID_HANDLE_VALUE;
491 proc[i].terminationEvent = INVALID_HANDLE_VALUE;
492 #endif
493 proc[i].pid = 0;
494 proc[i].state = FCGI_READY_STATE;
496 return proc;
499 int fcgi_util_ticks(struct timeval * tv)
501 #ifdef WIN32
502 // millisecs is sufficent granularity
503 DWORD millis = GetTickCount();
505 tv->tv_sec = millis / 1000;
506 tv->tv_usec = (millis % 1000) * 1000;
508 return 0;
509 #else
510 return gettimeofday(tv, NULL);
511 #endif