2 * $Id: fcgi_util.c,v 1.31 2004/01/07 01:56:00 robs Exp $
8 #pragma warning( disable : 4100 )
15 #if APR_HAVE_ARPA_INET_H
16 #include <arpa/inet.h>
23 fcgi_util_get_server_uid(const server_rec
* const s
)
27 #elif defined(APACHE2)
28 /* the main server's uid */
37 fcgi_util_get_server_gid(const server_rec
* const s
)
41 #elif defined(APACHE2)
42 /* the main server's gid */
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.
55 fcgi_util_socket_hash_filename(pool
*p
, const char *path
,
56 const char *user
, const char *group
)
58 char *buf
= ap_pstrcat(p
, path
, user
, group
, NULL
);
60 /* Canonicalize the path (remove "//", ".", "..") */
63 return ap_md5(p
, (unsigned char *)buf
);
67 /*******************************************************************************
68 * Concat src1 and src2 using the approprate path seperator for the platform.
70 static char * make_full_path(pool
*a
, const char *src1
, const char *src2
)
80 p
= ap_pstrcat(a
, "\\", src2
, NULL
);
82 else if (src1
[x
- 1] != '\\' && src1
[x
- 1] != '/') {
83 p
= ap_pstrcat(a
, src1
, "\\", src2
, NULL
);
86 p
= ap_pstrcat(a
, src1
, src2
, NULL
);
100 return ap_make_full_path(a
, src1
, src2
);
104 /*******************************************************************************
105 * Return absolute path to file in either "regular" FCGI socket directory or
106 * the dynamic directory. Result is allocated in pool p.
109 fcgi_util_socket_make_path_absolute(pool
* const p
,
110 const char *const file
, const int dynamic
)
113 if (ap_os_is_path_absolute(p
, (char *) file
))
115 if (ap_os_is_path_absolute(file
))
122 const char * parent_dir
= dynamic
? fcgi_dynamic_dir
: fcgi_socket_dir
;
123 return (const char *) make_full_path(p
, parent_dir
, file
);
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).
134 fcgi_util_socket_make_domain_addr(pool
*p
, struct sockaddr_un
**socket_addr
,
135 int *socket_addr_len
, const char *socket_path
)
137 int socket_pathLen
= strlen(socket_path
);
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
);
144 if (*socket_addr
== NULL
)
145 *socket_addr
= ap_pcalloc(p
, sizeof(struct sockaddr_un
));
147 memset(*socket_addr
, 0, sizeof(struct sockaddr_un
));
149 (*socket_addr
)->sun_family
= AF_UNIX
;
150 strcpy((*socket_addr
)->sun_path
, socket_path
);
152 *socket_addr_len
= SUN_LEN(*socket_addr
);
157 /*******************************************************************************
158 * Convert a hostname or IP address string to an in_addr struct.
161 convert_string_to_in_addr(const char * const hostname
, struct in_addr
* const addr
)
166 addr
->s_addr
= inet_addr((char *)hostname
);
168 #if !defined(INADDR_NONE) && defined(APACHE2)
169 #define INADDR_NONE APR_INADDR_NONE
172 if (addr
->s_addr
== INADDR_NONE
) {
173 if ((hp
= gethostbyname((char *)hostname
)) == NULL
)
176 memcpy((char *) addr
, hp
->h_addr
, hp
->h_length
);
178 while (hp
->h_addr_list
[count
] != 0)
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).
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
)
196 if (*socket_addr
== NULL
)
197 *socket_addr
= ap_pcalloc(p
, sizeof(struct sockaddr_in
));
199 memset(*socket_addr
, 0, sizeof(struct sockaddr_in
));
201 (*socket_addr
)->sin_family
= AF_INET
;
202 (*socket_addr
)->sin_port
= htons(port
);
204 /* Get an in_addr represention of the host */
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
);
211 (*socket_addr
)->sin_addr
.s_addr
= htonl(INADDR_ANY
);
214 *socket_addr_len
= sizeof(struct sockaddr_in
);
218 /*******************************************************************************
219 * Determine if a process with uid/gid can access a file with mode permissions.
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
)
226 struct stat myStatBuf
;
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
;
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";
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";
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"; */
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";
268 /* Get the user membership for the file's group. If the
269 * uid is a member, check the group bits. */
271 const struct group
* const gr
= getgrgid(statBuf
->st_gid
);
272 const struct passwd
* const pw
= getpwuid(uid
);
274 if (gr
!= NULL
&& pw
!= NULL
) {
275 char **user
= gr
->gr_mem
;
276 for ( ; *user
!= NULL
; user
++) {
277 if (strcmp(*user
, pw
->pw_name
) == 0) {
278 if (mode
& R_OK
&& !(statBuf
->st_mode
& S_IRGRP
))
279 return "read not allowed by group";
280 if (mode
& W_OK
&& !(statBuf
->st_mode
& S_IWGRP
))
281 return "write not allowed by group";
282 if (mode
& X_OK
&& !(statBuf
->st_mode
& S_IXGRP
))
283 return "execute not allowed by group";
290 /* That just leaves the other bits.. */
291 if (mode
& R_OK
&& !(statBuf
->st_mode
& S_IROTH
))
292 return "read not allowed";
293 if (mode
& W_OK
&& !(statBuf
->st_mode
& S_IWOTH
))
294 return "write not allowed";
295 if (mode
& X_OK
&& !(statBuf
->st_mode
& S_IXOTH
))
296 return "execute not allowed";
303 /*******************************************************************************
304 * Find a FastCGI server with a matching fs_path, and if fcgi_wrapper is
305 * enabled with matching uid and gid.
308 fcgi_util_fs_get_by_id(const char *ePath
, uid_t uid
, gid_t gid
)
310 char path
[FCGI_MAXPATH
];
313 /* @@@ This should now be done in the loop below */
314 ap_cpystrn(path
, ePath
, FCGI_MAXPATH
);
317 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
319 const char *fs_path
= s
->fs_path
;
320 for (i
= 0; fs_path
[i
] && path
[i
]; ++i
) {
321 if (fs_path
[i
] != path
[i
]) {
328 if (path
[i
] == '\0' || path
[i
] == '/') {
329 if (fcgi_wrapper
== NULL
|| (uid
== s
->uid
&& gid
== s
->gid
))
336 /*******************************************************************************
337 * Find a FastCGI server with a matching fs_path, and if fcgi_wrapper is
338 * enabled with matching user and group.
341 fcgi_util_fs_get(const char *ePath
, const char *user
, const char *group
)
343 char path
[FCGI_MAXPATH
];
346 ap_cpystrn(path
, ePath
, FCGI_MAXPATH
);
349 for (s
= fcgi_servers
; s
!= NULL
; s
= s
->next
) {
350 if (strcmp(s
->fs_path
, path
) == 0) {
351 if (fcgi_wrapper
== NULL
)
354 if (strcmp(user
, s
->user
) == 0
355 && (user
[0] == '~' || strcmp(group
, s
->group
) == 0))
365 fcgi_util_fs_is_path_ok(pool
* const p
, const char * const fs_path
, struct stat
*finfo
)
370 finfo
= (struct stat
*)ap_palloc(p
, sizeof(struct stat
));
371 if (stat(fs_path
, finfo
) < 0)
372 return ap_psprintf(p
, "stat(%s) failed: %s", fs_path
, strerror(errno
));
375 /* No Parse Header scripts aren't allowed.
376 * @@@ Well... we really could quite easily */
377 if (strncmp(strrchr(fs_path
, '/'), "/nph-", 5) == 0)
378 return ap_psprintf(p
, "NPH scripts cannot be run as FastCGI");
380 if (finfo
->st_mode
== 0)
381 return ap_psprintf(p
, "script not found or unable to stat()");
383 if (S_ISDIR(finfo
->st_mode
))
384 return ap_psprintf(p
, "script is a directory!");
386 /* Let the wrapper determine what it can and can't execute */
390 err
= fcgi_util_check_access(p
, fs_path
, finfo
, _S_IEXEC
, fcgi_user_id
, fcgi_group_id
);
392 err
= fcgi_util_check_access(p
, fs_path
, finfo
, X_OK
, fcgi_user_id
, fcgi_group_id
);
395 return ap_psprintf(p
,
396 "access for server (uid %ld, gid %ld) not allowed: %s",
397 (long)fcgi_user_id
, (long)fcgi_group_id
, err
);
406 /*******************************************************************************
407 * Allocate a new FastCGI server record from pool p with default values.
410 fcgi_util_fs_new(pool
*p
)
412 fcgi_server
*s
= (fcgi_server
*) ap_pcalloc(p
, sizeof(fcgi_server
));
414 /* Initialize anything who's init state is not zeroizzzzed */
415 s
->listenQueueDepth
= FCGI_DEFAULT_LISTEN_Q
;
416 s
->appConnectTimeout
= FCGI_DEFAULT_APP_CONN_TIMEOUT
;
417 s
->idle_timeout
= FCGI_DEFAULT_IDLE_TIMEOUT
;
418 s
->initStartDelay
= DEFAULT_INIT_START_DELAY
;
419 s
->restartDelay
= FCGI_DEFAULT_RESTART_DELAY
;
420 s
->minServerLife
= FCGI_DEFAULT_MIN_SERVER_LIFE
;
421 s
->restartOnExit
= FALSE
;
422 s
->directive
= APP_CLASS_UNKNOWN
;
423 s
->processPriority
= FCGI_DEFAULT_PRIORITY
;
424 s
->envp
= &fcgi_empty_env
;
427 s
->listenFd
= (int) INVALID_HANDLE_VALUE
;
435 /*******************************************************************************
436 * Add the server to the linked list of FastCGI servers.
439 fcgi_util_fs_add(fcgi_server
*s
)
441 s
->next
= fcgi_servers
;
445 /*******************************************************************************
446 * Configure uid, gid, user, group, username for wrapper.
449 fcgi_util_fs_set_uid_n_gid(pool
*p
, fcgi_server
*s
, uid_t uid
, gid_t gid
)
456 if (fcgi_wrapper
== NULL
)
459 if (uid
== 0 || gid
== 0) {
460 return "invalid uid or gid, see the -user and -group options";
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
));
471 s
->user
= ap_pstrdup(p
, pw
->pw_name
);
472 s
->username
= s
->user
;
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
));
482 s
->group
= ap_pstrdup(p
, gr
->gr_name
);
489 /*******************************************************************************
490 * Allocate an array of ServerProcess records.
493 fcgi_util_fs_create_procs(pool
*p
, int num
)
496 ServerProcess
*proc
= (ServerProcess
*)ap_pcalloc(p
, sizeof(ServerProcess
) * num
);
498 for (i
= 0; i
< num
; i
++) {
500 proc
[i
].handle
= INVALID_HANDLE_VALUE
;
501 proc
[i
].terminationEvent
= INVALID_HANDLE_VALUE
;
504 proc
[i
].state
= FCGI_READY_STATE
;
509 int fcgi_util_ticks(struct timeval
* tv
)
512 /* millisecs is sufficent granularity */
513 DWORD millis
= GetTickCount();
515 tv
->tv_sec
= millis
/ 1000;
516 tv
->tv_usec
= (millis
% 1000) * 1000;
520 return gettimeofday(tv
, NULL
);