Add -user and -group options to FastCgiServer and FastCgiExternalServer.
[mod_fastcgi.git] / fcgi_util.c
blob179a9fba4bd77fc1e8b86a8d878e3f190f0c942c
1 /*
2 * $Id: fcgi_util.c,v 1.26 2002/09/22 16:53:13 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 #endif
16 uid_t
17 fcgi_util_get_server_uid(const server_rec * const s)
19 #ifdef APACHE2
20 /* AP2TODO get the server's uid */
21 return 0;
22 #else
23 return s->server_uid;
24 #endif
27 uid_t
28 fcgi_util_get_server_gid(const server_rec * const s)
30 #ifdef APACHE2
31 /* AP2TODO get the server's gid */
32 return 0;
33 #else
34 return s->server_gid;
35 #endif
38 /*******************************************************************************
39 * Compute printable MD5 hash. Pool p is used for scratch as well as for
40 * allocating the hash - use temp storage, and dup it if you need to keep it.
42 char *
43 fcgi_util_socket_hash_filename(pool *p, const char *path,
44 const char *user, const char *group)
46 char *buf = ap_pstrcat(p, path, user, group, NULL);
48 /* Canonicalize the path (remove "//", ".", "..") */
49 ap_getparents(buf);
51 return ap_md5(p, (unsigned char *)buf);
55 /*******************************************************************************
56 * Concat src1 and src2 using the approprate path seperator for the platform.
58 static char * make_full_path(pool *a, const char *src1, const char *src2)
60 #ifdef WIN32
61 register int x;
62 char * p ;
63 char * q ;
65 x = strlen(src1);
67 if (x == 0) {
68 p = ap_pstrcat(a, "\\", src2, NULL);
70 else if (src1[x - 1] != '\\' && src1[x - 1] != '/') {
71 p = ap_pstrcat(a, src1, "\\", src2, NULL);
73 else {
74 p = ap_pstrcat(a, src1, src2, NULL);
77 q = p ;
78 while (*q)
80 if (*q == '/') {
81 *q = '\\' ;
83 ++q;
86 return p ;
87 #else
88 return ap_make_full_path(a, src1, src2);
89 #endif
92 /*******************************************************************************
93 * Return absolute path to file in either "regular" FCGI socket directory or
94 * the dynamic directory. Result is allocated in pool p.
96 const char *
97 fcgi_util_socket_make_path_absolute(pool * const p,
98 const char *const file, const int dynamic)
100 #ifdef APACHE2
101 if (ap_os_is_path_absolute(p, (char *) file))
102 #else
103 if (ap_os_is_path_absolute(file))
104 #endif
106 return file;
108 else
110 const char * parent_dir = dynamic ? fcgi_dynamic_dir : fcgi_socket_dir;
111 return (const char *) make_full_path(p, parent_dir, file);
115 #ifndef WIN32
116 /*******************************************************************************
117 * Build a Domain Socket Address structure, and calculate its size.
118 * The error message is allocated from the pool p. If you don't want the
119 * struct sockaddr_un also allocated from p, pass it preallocated (!=NULL).
121 const char *
122 fcgi_util_socket_make_domain_addr(pool *p, struct sockaddr_un **socket_addr,
123 int *socket_addr_len, const char *socket_path)
125 int socket_pathLen = strlen(socket_path);
127 if (socket_pathLen >= sizeof((*socket_addr)->sun_path)) {
128 return ap_pstrcat(p, "path \"", socket_path,
129 "\" is too long for a Domain socket", NULL);
132 if (*socket_addr == NULL)
133 *socket_addr = ap_pcalloc(p, sizeof(struct sockaddr_un));
134 else
135 memset(*socket_addr, 0, sizeof(struct sockaddr_un));
137 (*socket_addr)->sun_family = AF_UNIX;
138 strcpy((*socket_addr)->sun_path, socket_path);
140 *socket_addr_len = SUN_LEN(*socket_addr);
141 return NULL;
143 #endif
145 /*******************************************************************************
146 * Convert a hostname or IP address string to an in_addr struct.
148 static int
149 convert_string_to_in_addr(const char * const hostname, struct in_addr * const addr)
151 struct hostent *hp;
152 int count;
154 addr->s_addr = inet_addr((char *)hostname);
156 #if !defined(INADDR_NONE) && defined(APACHE2)
157 #define INADDR_NONE APR_INADDR_NONE
158 #endif
160 if (addr->s_addr == INADDR_NONE) {
161 if ((hp = gethostbyname((char *)hostname)) == NULL)
162 return -1;
164 memcpy((char *) addr, hp->h_addr, hp->h_length);
165 count = 0;
166 while (hp->h_addr_list[count] != 0)
167 count++;
169 return count;
171 return 1;
175 /*******************************************************************************
176 * Build an Inet Socket Address structure, and calculate its size.
177 * The error message is allocated from the pool p. If you don't want the
178 * struct sockaddr_in also allocated from p, pass it preallocated (!=NULL).
180 const char *
181 fcgi_util_socket_make_inet_addr(pool *p, struct sockaddr_in **socket_addr,
182 int *socket_addr_len, const char *host, unsigned short port)
184 if (*socket_addr == NULL)
185 *socket_addr = ap_pcalloc(p, sizeof(struct sockaddr_in));
186 else
187 memset(*socket_addr, 0, sizeof(struct sockaddr_in));
189 (*socket_addr)->sin_family = AF_INET;
190 (*socket_addr)->sin_port = htons(port);
192 /* Get an in_addr represention of the host */
193 if (host != NULL) {
194 if (convert_string_to_in_addr(host, &(*socket_addr)->sin_addr) != 1) {
195 return ap_pstrcat(p, "failed to resolve \"", host,
196 "\" to exactly one IP address", NULL);
198 } else {
199 (*socket_addr)->sin_addr.s_addr = htonl(INADDR_ANY);
202 *socket_addr_len = sizeof(struct sockaddr_in);
203 return NULL;
206 /*******************************************************************************
207 * Determine if a process with uid/gid can access a file with mode permissions.
209 const char *
210 fcgi_util_check_access(pool *tp,
211 const char * const path, const struct stat *statBuf,
212 const int mode, const uid_t uid, const gid_t gid)
214 struct stat myStatBuf;
216 if (statBuf == NULL) {
217 if (stat(path, &myStatBuf) < 0)
218 return ap_psprintf(tp, "stat(%s) failed: %s", path, strerror(errno));
219 statBuf = &myStatBuf;
222 #ifndef WIN32
223 /* If the uid owns the file, check the owner bits */
224 if (uid == statBuf->st_uid) {
225 if (mode & R_OK && !(statBuf->st_mode & S_IRUSR))
226 return "read not allowed by owner";
227 if (mode & W_OK && !(statBuf->st_mode & S_IWUSR))
228 return "write not allowed by owner";
229 if (mode & X_OK && !(statBuf->st_mode & S_IXUSR))
230 return "execute not allowed by owner";
231 return NULL;
233 #else
234 if (mode & _S_IREAD && !(statBuf->st_mode & _S_IREAD))
235 return "read not allowed";
236 if (mode & _S_IWRITE && !(statBuf->st_mode & _S_IWRITE))
237 return "write not allowed";
239 // I don't think this works on FAT, but since I don't know how to check..
240 // if (mode & _S_IEXEC && !(statBuf->st_mode & _S_IEXEC))
241 // return "execute not allowed";
242 #endif
244 #if !defined(__EMX__) && !defined(WIN32)
245 /* If the gid is same as the file's group, check the group bits */
246 if (gid == statBuf->st_gid) {
247 if (mode & R_OK && !(statBuf->st_mode & S_IRGRP))
248 return "read not allowed by group";
249 if (mode & W_OK && !(statBuf->st_mode & S_IWGRP))
250 return "write not allowed by group";
251 if (mode & X_OK && !(statBuf->st_mode & S_IXGRP))
252 return "execute not allowed by group";
253 return NULL;
256 /* Get the user membership for the file's group. If the
257 * uid is a member, check the group bits. */
259 const struct group * const gr = getgrgid(statBuf->st_gid);
260 const struct passwd * const pw = getpwuid(uid);
262 if (gr != NULL && pw != NULL) {
263 char **user = gr->gr_mem;
264 for ( ; *user != NULL; user++) {
265 if (strcmp(*user, pw->pw_name) == 0) {
266 if (mode & R_OK && !(statBuf->st_mode & S_IRGRP))
267 return "read not allowed by group";
268 if (mode & W_OK && !(statBuf->st_mode & S_IWGRP))
269 return "write not allowed by group";
270 if (mode & X_OK && !(statBuf->st_mode & S_IXGRP))
271 return "execute not allowed by group";
272 return NULL;
278 /* That just leaves the other bits.. */
279 if (mode & R_OK && !(statBuf->st_mode & S_IROTH))
280 return "read not allowed";
281 if (mode & W_OK && !(statBuf->st_mode & S_IWOTH))
282 return "write not allowed";
283 if (mode & X_OK && !(statBuf->st_mode & S_IXOTH))
284 return "execute not allowed";
285 #endif
287 return NULL;
291 /*******************************************************************************
292 * Find a FastCGI server with a matching fs_path, and if fcgi_wrapper is
293 * enabled with matching uid and gid.
295 fcgi_server *
296 fcgi_util_fs_get_by_id(const char *ePath, uid_t uid, gid_t gid)
298 char path[FCGI_MAXPATH];
299 fcgi_server *s;
301 /* @@@ This should now be done in the loop below */
302 ap_cpystrn(path, ePath, FCGI_MAXPATH);
303 ap_no2slash(path);
305 for (s = fcgi_servers; s != NULL; s = s->next) {
306 int i;
307 const char *fs_path = s->fs_path;
308 for (i = 0; fs_path[i] && path[i]; ++i) {
309 if (fs_path[i] != path[i]) {
310 break;
313 if (fs_path[i]) {
314 continue;
316 if (path[i] == '\0' || path[i] == '/') {
317 if (fcgi_wrapper == NULL || (uid == s->uid && gid == s->gid))
318 return s;
321 return NULL;
324 /*******************************************************************************
325 * Find a FastCGI server with a matching fs_path, and if fcgi_wrapper is
326 * enabled with matching user and group.
328 fcgi_server *
329 fcgi_util_fs_get(const char *ePath, const char *user, const char *group)
331 char path[FCGI_MAXPATH];
332 fcgi_server *s;
334 ap_cpystrn(path, ePath, FCGI_MAXPATH);
335 ap_no2slash(path);
337 for (s = fcgi_servers; s != NULL; s = s->next) {
338 if (strcmp(s->fs_path, path) == 0) {
339 if (fcgi_wrapper == NULL)
340 return s;
342 if (strcmp(user, s->user) == 0
343 && (user[0] == '~' || strcmp(group, s->group) == 0))
345 return s;
349 return NULL;
352 const char *
353 fcgi_util_fs_is_path_ok(pool * const p, const char * const fs_path, struct stat *finfo)
355 const char *err;
357 if (finfo == NULL) {
358 finfo = (struct stat *)ap_palloc(p, sizeof(struct stat));
359 if (stat(fs_path, finfo) < 0)
360 return ap_psprintf(p, "stat(%s) failed: %s", fs_path, strerror(errno));
363 /* No Parse Header scripts aren't allowed.
364 * @@@ Well... we really could quite easily */
365 if (strncmp(strrchr(fs_path, '/'), "/nph-", 5) == 0)
366 return ap_psprintf(p, "NPH scripts cannot be run as FastCGI");
368 if (finfo->st_mode == 0)
369 return ap_psprintf(p, "script not found or unable to stat()");
371 if (S_ISDIR(finfo->st_mode))
372 return ap_psprintf(p, "script is a directory!");
374 /* Let the wrapper determine what it can and can't execute */
375 if (! fcgi_wrapper)
377 #ifdef WIN32
378 err = fcgi_util_check_access(p, fs_path, finfo, _S_IEXEC, fcgi_user_id, fcgi_group_id);
379 #else
380 err = fcgi_util_check_access(p, fs_path, finfo, X_OK, fcgi_user_id, fcgi_group_id);
381 #endif
382 if (err) {
383 return ap_psprintf(p,
384 "access for server (uid %ld, gid %ld) not allowed: %s",
385 (long)fcgi_user_id, (long)fcgi_group_id, err);
389 return NULL;
394 /*******************************************************************************
395 * Allocate a new FastCGI server record from pool p with default values.
397 fcgi_server *
398 fcgi_util_fs_new(pool *p)
400 fcgi_server *s = (fcgi_server *) ap_pcalloc(p, sizeof(fcgi_server));
402 /* Initialize anything who's init state is not zeroizzzzed */
403 s->listenQueueDepth = FCGI_DEFAULT_LISTEN_Q;
404 s->appConnectTimeout = FCGI_DEFAULT_APP_CONN_TIMEOUT;
405 s->idle_timeout = FCGI_DEFAULT_IDLE_TIMEOUT;
406 s->initStartDelay = DEFAULT_INIT_START_DELAY;
407 s->restartDelay = FCGI_DEFAULT_RESTART_DELAY;
408 s->restartOnExit = FALSE;
409 s->directive = APP_CLASS_UNKNOWN;
410 s->processPriority = FCGI_DEFAULT_PRIORITY;
411 s->envp = &fcgi_empty_env;
413 #ifdef WIN32
414 s->listenFd = (int) INVALID_HANDLE_VALUE;
415 #else
416 s->listenFd = -2;
417 #endif
419 return s;
422 /*******************************************************************************
423 * Add the server to the linked list of FastCGI servers.
425 void
426 fcgi_util_fs_add(fcgi_server *s)
428 s->next = fcgi_servers;
429 fcgi_servers = s;
432 /*******************************************************************************
433 * Configure uid, gid, user, group, username for wrapper.
435 const char *
436 fcgi_util_fs_set_uid_n_gid(pool *p, fcgi_server *s, uid_t uid, gid_t gid)
438 #ifndef WIN32
440 struct passwd *pw;
441 struct group *gr;
443 if (fcgi_wrapper == NULL)
444 return NULL;
446 if (uid == 0 || gid == 0) {
447 return "invalid uid or gid, see the -user and -group options";
450 s->uid = uid;
451 pw = getpwuid(uid);
452 if (pw == NULL) {
453 return ap_psprintf(p,
454 "getpwuid() couldn't determine the username for uid '%ld', "
455 "you probably need to modify the User directive: %s",
456 (long)uid, strerror(errno));
458 s->user = ap_pstrdup(p, pw->pw_name);
459 s->username = s->user;
461 s->gid = gid;
462 gr = getgrgid(gid);
463 if (gr == NULL) {
464 return ap_psprintf(p,
465 "getgrgid() couldn't determine the group name for gid '%ld', "
466 "you probably need to modify the Group directive: %s",
467 (long)gid, strerror(errno));
469 s->group = ap_pstrdup(p, gr->gr_name);
471 #endif /* !WIN32 */
473 return NULL;
476 /*******************************************************************************
477 * Allocate an array of ServerProcess records.
479 ServerProcess *
480 fcgi_util_fs_create_procs(pool *p, int num)
482 int i;
483 ServerProcess *proc = (ServerProcess *)ap_pcalloc(p, sizeof(ServerProcess) * num);
485 for (i = 0; i < num; i++) {
486 #ifdef WIN32
487 proc[i].handle = INVALID_HANDLE_VALUE;
488 proc[i].terminationEvent = INVALID_HANDLE_VALUE;
489 #endif
490 proc[i].pid = 0;
491 proc[i].state = FCGI_READY_STATE;
493 return proc;
496 int fcgi_util_ticks(struct timeval * tv)
498 #ifdef WIN32
499 // millisecs is sufficent granularity
500 DWORD millis = GetTickCount();
502 tv->tv_sec = millis / 1000;
503 tv->tv_usec = (millis % 1000) * 1000;
505 return 0;
506 #else
507 return gettimeofday(tv, NULL);
508 #endif