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