1 /*
2  * $Id: fcgi_util.c,v 1.32 2007/09/23 16:33:29 robs Exp $
3  */
4 
5 #include "fcgi.h"
6 
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 
15 #if APR_HAVE_ARPA_INET_H
16 #include <arpa/inet.h>
17 #endif
18 
19 #include "unixd.h"
20 #endif
21 
22 uid_t
fcgi_util_get_server_uid(const server_rec * const s)23 fcgi_util_get_server_uid(const server_rec * const s)
24 {
25 #if defined(WIN32)
26     return (uid_t) 0;
27 #elif defined(APACHE2)
28     /* the main server's uid */
29     return ap_user_id;
30 #else
31     /* the vhost's uid */
32     return s->server_uid;
33 #endif
34 }
35 
36 uid_t
fcgi_util_get_server_gid(const server_rec * const s)37 fcgi_util_get_server_gid(const server_rec * const s)
38 {
39 #if defined(WIN32)
40     return (uid_t) 0;
41 #elif defined(APACHE2)
42     /* the main server's gid */
43     return ap_group_id;
44 #else
45     /* the vhost's gid */
46     return s->server_gid;
47 #endif
48 }
49 
50 /*******************************************************************************
51  * Compute printable MD5 hash. Pool p is used for scratch as well as for
52  * allocating the hash - use temp storage, and dup it if you need to keep it.
53  */
54 char *
fcgi_util_socket_hash_filename(pool * p,const char * path,const char * user,const char * group)55 fcgi_util_socket_hash_filename(pool *p, const char *path,
56         const char *user, const char *group)
57 {
58     char *buf = ap_pstrcat(p, path, user, group, NULL);
59 
60     /* Canonicalize the path (remove "//", ".", "..") */
61     ap_getparents(buf);
62 
63     return ap_md5(p, (unsigned char *)buf);
64 }
65 
66 
67  /*******************************************************************************
68   * Concat src1 and src2 using the approprate path seperator for the platform.
69   */
make_full_path(pool * a,const char * src1,const char * src2)70 static char * make_full_path(pool *a, const char *src1, const char *src2)
71 {
72 #ifdef WIN32
73     register int x;
74     char * p ;
75     char * q ;
76 
77     x = strlen(src1);
78 
79     if (x == 0) {
80 	    p = ap_pstrcat(a, "\\", src2, NULL);
81     }
82     else if (src1[x - 1] != '\\' && src1[x - 1] != '/') {
83 	    p = ap_pstrcat(a, src1, "\\", src2, NULL);
84     }
85     else {
86 	    p = ap_pstrcat(a, src1, src2, NULL);
87     }
88 
89     q = p ;
90     while (*q)
91 	{
92         if (*q == '/') {
93 	        *q = '\\' ;
94         }
95 	    ++q;
96 	}
97 
98     return p ;
99 #else
100     return ap_make_full_path(a, src1, src2);
101 #endif
102 }
103 
104 /*******************************************************************************
105  * Return absolute path to file in either "regular" FCGI socket directory or
106  * the dynamic directory.  Result is allocated in pool p.
107  */
108 const char *
fcgi_util_socket_make_path_absolute(pool * const p,const char * const file,const int dynamic)109 fcgi_util_socket_make_path_absolute(pool * const p,
110         const char *const file, const int dynamic)
111 {
112 #ifdef APACHE2
113     if (ap_os_is_path_absolute(p, (char *) file))
114 #else
115     if (ap_os_is_path_absolute(file))
116 #endif
117     {
118 	    return file;
119     }
120     else
121     {
122         const char * parent_dir = dynamic ? fcgi_dynamic_dir : fcgi_socket_dir;
123         return (const char *) make_full_path(p, parent_dir, file);
124     }
125 }
126 
127 #ifndef WIN32
128 /*******************************************************************************
129  * Build a Domain Socket Address structure, and calculate its size.
130  * The error message is allocated from the pool p.  If you don't want the
131  * struct sockaddr_un also allocated from p, pass it preallocated (!=NULL).
132  */
133 const char *
fcgi_util_socket_make_domain_addr(pool * p,struct sockaddr_un ** socket_addr,int * socket_addr_len,const char * socket_path)134 fcgi_util_socket_make_domain_addr(pool *p, struct sockaddr_un **socket_addr,
135         int *socket_addr_len, const char *socket_path)
136 {
137     int socket_pathLen = strlen(socket_path);
138 
139     if (socket_pathLen >= sizeof((*socket_addr)->sun_path)) {
140         return ap_pstrcat(p, "path \"", socket_path,
141                        "\" is too long for a Domain socket", NULL);
142     }
143 
144     if (*socket_addr == NULL)
145         *socket_addr = ap_pcalloc(p, sizeof(struct sockaddr_un));
146     else
147         memset(*socket_addr, 0, sizeof(struct sockaddr_un));
148 
149     (*socket_addr)->sun_family = AF_UNIX;
150     strcpy((*socket_addr)->sun_path, socket_path);
151 
152     *socket_addr_len = SUN_LEN(*socket_addr);
153     return NULL;
154 }
155 #endif
156 
157 /*******************************************************************************
158  * Convert a hostname or IP address string to an in_addr struct.
159  */
160 static int
convert_string_to_in_addr(const char * const hostname,struct in_addr * const addr)161 convert_string_to_in_addr(const char * const hostname, struct in_addr * const addr)
162 {
163     struct hostent *hp;
164     int count;
165 
166     addr->s_addr = inet_addr((char *)hostname);
167 
168 #if !defined(INADDR_NONE) && defined(APACHE2)
169 #define INADDR_NONE APR_INADDR_NONE
170 #endif
171 
172     if (addr->s_addr == INADDR_NONE) {
173         if ((hp = gethostbyname((char *)hostname)) == NULL)
174             return -1;
175 
176         memcpy((char *) addr, hp->h_addr, hp->h_length);
177         count = 0;
178         while (hp->h_addr_list[count] != 0)
179             count++;
180 
181         return count;
182     }
183     return 1;
184 }
185 
186 
187 /*******************************************************************************
188  * Build an Inet Socket Address structure, and calculate its size.
189  * The error message is allocated from the pool p. If you don't want the
190  * struct sockaddr_in also allocated from p, pass it preallocated (!=NULL).
191  */
192 const char *
fcgi_util_socket_make_inet_addr(pool * p,struct sockaddr_in ** socket_addr,int * socket_addr_len,const char * host,unsigned short port)193 fcgi_util_socket_make_inet_addr(pool *p, struct sockaddr_in **socket_addr,
194         int *socket_addr_len, const char *host, unsigned short port)
195 {
196     if (*socket_addr == NULL)
197         *socket_addr = ap_pcalloc(p, sizeof(struct sockaddr_in));
198     else
199         memset(*socket_addr, 0, sizeof(struct sockaddr_in));
200 
201     (*socket_addr)->sin_family = AF_INET;
202     (*socket_addr)->sin_port = htons(port);
203 
204     /* Get an in_addr represention of the host */
205     if (host != NULL) {
206         if (convert_string_to_in_addr(host, &(*socket_addr)->sin_addr) != 1) {
207             return ap_pstrcat(p, "failed to resolve \"", host,
208                            "\" to exactly one IP address", NULL);
209         }
210     } else {
211       (*socket_addr)->sin_addr.s_addr = htonl(INADDR_ANY);
212     }
213 
214     *socket_addr_len = sizeof(struct sockaddr_in);
215     return NULL;
216 }
217 
218 /*******************************************************************************
219  * Determine if a process with uid/gid can access a file with mode permissions.
220  */
221 const char *
fcgi_util_check_access(pool * tp,const char * const path,const struct stat * statBuf,const int mode,const uid_t uid,const gid_t gid)222 fcgi_util_check_access(pool *tp,
223         const char * const path, const struct stat *statBuf,
224         const int mode, const uid_t uid, const gid_t gid)
225 {
226     struct stat myStatBuf;
227 
228     if (statBuf == NULL) {
229         if (stat(path, &myStatBuf) < 0)
230             return ap_psprintf(tp, "stat(%s) failed: %s", path, strerror(errno));
231         statBuf = &myStatBuf;
232     }
233 
234 #ifndef WIN32
235     /* If the uid owns the file, check the owner bits */
236     if (uid == statBuf->st_uid) {
237         if (mode & R_OK && !(statBuf->st_mode & S_IRUSR))
238             return "read not allowed by owner";
239         if (mode & W_OK && !(statBuf->st_mode & S_IWUSR))
240             return "write not allowed by owner";
241         if (mode & X_OK && !(statBuf->st_mode & S_IXUSR))
242             return "execute not allowed by owner";
243         return NULL;
244     }
245 #else
246     if (mode & _S_IREAD && !(statBuf->st_mode & _S_IREAD))
247         return "read not allowed";
248     if (mode & _S_IWRITE && !(statBuf->st_mode & _S_IWRITE))
249         return "write not allowed";
250 
251     /* I don't think this works on FAT, but since I don't know how to check..
252      * if (mode & _S_IEXEC && !(statBuf->st_mode & _S_IEXEC))
253      *     return "execute not allowed"; */
254 #endif
255 
256 #if  !defined(__EMX__) && !defined(WIN32)
257     /* If the gid is same as the file's group, check the group bits */
258     if (gid == statBuf->st_gid) {
259         if (mode & R_OK && !(statBuf->st_mode & S_IRGRP))
260             return "read not allowed by group";
261         if (mode & W_OK && !(statBuf->st_mode & S_IWGRP))
262             return "write not allowed by group";
263         if (mode & X_OK && !(statBuf->st_mode & S_IXGRP))
264             return "execute not allowed by group";
265         return NULL;
266     }
267 
268     /* Get the user membership for the file's group.  If the
269      * uid is a member, check the group bits. */
270     {
271 	char buf[1024], buf2[1024];
272 	struct group gr, *r;
273 	struct passwd pw, *r2;
274 
275 	getgrgid_r(statBuf->st_gid, &gr, buf, sizeof(buf), &r);
276 	getpwuid_r(uid, &pw, buf2, sizeof(buf2), &r2);
277 
278         if (r != NULL && r2 != NULL) {
279             char **user = r->gr_mem;
280             for ( ; *user != NULL; user++) {
281                 if (strcmp(*user, r2->pw_name) == 0) {
282                     if (mode & R_OK && !(statBuf->st_mode & S_IRGRP))
283                         return "read not allowed by group";
284                     if (mode & W_OK && !(statBuf->st_mode & S_IWGRP))
285                         return "write not allowed by group";
286                     if (mode & X_OK && !(statBuf->st_mode & S_IXGRP))
287                         return "execute not allowed by group";
288                     return NULL;
289                 }
290             }
291         }
292     }
293 
294     /* That just leaves the other bits.. */
295     if (mode & R_OK && !(statBuf->st_mode & S_IROTH))
296         return "read not allowed";
297     if (mode & W_OK && !(statBuf->st_mode & S_IWOTH))
298         return "write not allowed";
299     if (mode & X_OK && !(statBuf->st_mode & S_IXOTH))
300         return "execute not allowed";
301 #endif
302 
303     return NULL;
304 }
305 
306 
307 /*******************************************************************************
308  * Find a FastCGI server with a matching fs_path, and if fcgi_wrapper is
309  * enabled with matching uid and gid.
310  */
311 fcgi_server *
fcgi_util_fs_get_by_id(const char * ePath,uid_t uid,gid_t gid)312 fcgi_util_fs_get_by_id(const char *ePath, uid_t uid, gid_t gid)
313 {
314     char path[FCGI_MAXPATH];
315     fcgi_server *s;
316 
317     /* @@@ This should now be done in the loop below */
318     ap_cpystrn(path, ePath, FCGI_MAXPATH);
319     ap_no2slash(path);
320 
321     for (s = fcgi_servers; s != NULL; s = s->next) {
322         int i;
323         const char *fs_path = s->fs_path;
324         for (i = 0; fs_path[i] && path[i]; ++i) {
325             if (fs_path[i] != path[i]) {
326                 break;
327             }
328         }
329         if (fs_path[i]) {
330             continue;
331         }
332         if (path[i] == '\0' || path[i] == '/') {
333         if (fcgi_wrapper == NULL || (uid == s->uid && gid == s->gid))
334             return s;
335         }
336     }
337     return NULL;
338 }
339 
340 /*******************************************************************************
341  * Find a FastCGI server with a matching fs_path, and if fcgi_wrapper is
342  * enabled with matching user and group.
343  */
344 fcgi_server *
fcgi_util_fs_get(const char * ePath,const char * user,const char * group)345 fcgi_util_fs_get(const char *ePath, const char *user, const char *group)
346 {
347     char path[FCGI_MAXPATH];
348     fcgi_server *s;
349 
350     ap_cpystrn(path, ePath, FCGI_MAXPATH);
351     ap_no2slash(path);
352 
353     for (s = fcgi_servers; s != NULL; s = s->next) {
354         if (strcmp(s->fs_path, path) == 0) {
355             if (fcgi_wrapper == NULL)
356                 return s;
357 
358             if (strcmp(user, s->user) == 0
359                 && (user[0] == '~' || strcmp(group, s->group) == 0))
360             {
361                 return s;
362             }
363         }
364     }
365     return NULL;
366 }
367 
368 const char *
fcgi_util_fs_is_path_ok(pool * const p,const char * const fs_path,struct stat * finfo)369 fcgi_util_fs_is_path_ok(pool * const p, const char * const fs_path, struct stat *finfo)
370 {
371     const char *err;
372 
373     if (finfo == NULL) {
374         finfo = (struct stat *)ap_palloc(p, sizeof(struct stat));
375         if (stat(fs_path, finfo) < 0)
376             return ap_psprintf(p, "stat(%s) failed: %s", fs_path, strerror(errno));
377     }
378 
379     if (finfo->st_mode == 0)
380         return ap_psprintf(p, "script not found or unable to stat()");
381 
382     if (S_ISDIR(finfo->st_mode))
383         return ap_psprintf(p, "script is a directory!");
384 
385     /* Let the wrapper determine what it can and can't execute */
386     if (! fcgi_wrapper)
387     {
388 #ifdef WIN32
389         err = fcgi_util_check_access(p, fs_path, finfo, _S_IEXEC, fcgi_user_id, fcgi_group_id);
390 #else
391         err = fcgi_util_check_access(p, fs_path, finfo, X_OK, fcgi_user_id, fcgi_group_id);
392 #endif
393         if (err) {
394             return ap_psprintf(p,
395                 "access for server (uid %ld, gid %ld) not allowed: %s",
396                 (long)fcgi_user_id, (long)fcgi_group_id, err);
397         }
398     }
399 
400     return NULL;
401 }
402 
403 
404 
405 /*******************************************************************************
406  * Allocate a new FastCGI server record from pool p with default values.
407  */
408 fcgi_server *
fcgi_util_fs_new(pool * p)409 fcgi_util_fs_new(pool *p)
410 {
411     fcgi_server *s = (fcgi_server *) ap_pcalloc(p, sizeof(fcgi_server));
412 
413     /* Initialize anything who's init state is not zeroizzzzed */
414     s->listenQueueDepth = FCGI_DEFAULT_LISTEN_Q;
415     s->appConnectTimeout = FCGI_DEFAULT_APP_CONN_TIMEOUT;
416     s->idle_timeout = FCGI_DEFAULT_IDLE_TIMEOUT;
417     s->initStartDelay = DEFAULT_INIT_START_DELAY;
418     s->restartDelay = FCGI_DEFAULT_RESTART_DELAY;
419 	s->minServerLife = FCGI_DEFAULT_MIN_SERVER_LIFE;
420     s->restartOnExit = FALSE;
421     s->directive = APP_CLASS_UNKNOWN;
422     s->processPriority = FCGI_DEFAULT_PRIORITY;
423     s->envp = &fcgi_empty_env;
424 
425 #ifdef WIN32
426     s->listenFd = (int) INVALID_HANDLE_VALUE;
427 #else
428     s->listenFd = -2;
429 #endif
430 
431     return s;
432 }
433 
434 /*******************************************************************************
435  * Add the server to the linked list of FastCGI servers.
436  */
437 void
fcgi_util_fs_add(fcgi_server * s)438 fcgi_util_fs_add(fcgi_server *s)
439 {
440     s->next = fcgi_servers;
441     fcgi_servers = s;
442 }
443 
444 /*******************************************************************************
445  * Configure uid, gid, user, group, username for wrapper.
446  */
447 const char *
fcgi_util_fs_set_uid_n_gid(pool * p,fcgi_server * s,uid_t uid,gid_t gid)448 fcgi_util_fs_set_uid_n_gid(pool *p, fcgi_server *s, uid_t uid, gid_t gid)
449 {
450 #ifndef WIN32
451 
452     char buf[1024];
453     struct passwd *pw;
454     struct group gr, *r;
455 
456     if (fcgi_wrapper == NULL)
457         return NULL;
458 
459     if (uid == 0 || gid == 0) {
460         return "invalid uid or gid, see the -user and -group options";
461     }
462 
463     s->uid = uid;
464     pw = getpwuid(uid);
465     if (pw == NULL) {
466         return ap_psprintf(p,
467             "getpwuid() couldn't determine the username for uid '%ld', "
468             "you probably need to modify the User directive: %s",
469             (long)uid, strerror(errno));
470     }
471     s->user = ap_pstrdup(p, pw->pw_name);
472     s->username = s->user;
473 
474     s->gid = gid;
475     getgrgid_r(gid, &gr, buf, sizeof(buf), &r);
476     if (r == NULL) {
477         return ap_psprintf(p,
478             "getgrgid() couldn't determine the group name for gid '%ld', "
479             "you probably need to modify the Group directive: %s",
480             (long)gid, strerror(errno));
481     }
482     s->group = ap_pstrdup(p, r->gr_name);
483 
484 #endif /* !WIN32 */
485 
486     return NULL;
487 }
488 
489 /*******************************************************************************
490  * Allocate an array of ServerProcess records.
491  */
492 ServerProcess *
fcgi_util_fs_create_procs(pool * p,int num)493 fcgi_util_fs_create_procs(pool *p, int num)
494 {
495     int i;
496     ServerProcess *proc = (ServerProcess *)ap_pcalloc(p, sizeof(ServerProcess) * num);
497 
498     for (i = 0; i < num; i++) {
499 #ifdef WIN32
500         proc[i].handle = INVALID_HANDLE_VALUE;
501         proc[i].terminationEvent = INVALID_HANDLE_VALUE;
502 #endif
503         proc[i].pid = 0;
504         proc[i].state = FCGI_READY_STATE;
505     }
506     return proc;
507 }
508 
fcgi_util_ticks(struct timeval * tv)509 int fcgi_util_ticks(struct timeval * tv)
510 {
511 #ifdef WIN32
512     /* millisecs is sufficent granularity */
513     DWORD millis = GetTickCount();
514 
515     tv->tv_sec = millis / 1000;
516     tv->tv_usec = (millis % 1000) * 1000;
517 
518     return 0;
519 #else
520     return gettimeofday(tv, NULL);
521 #endif
522 }
523 
524 
525