Put in links to suexec and cgiwrap.
[mod_fastcgi.git] / fcgi_util.c
blobf64c61f07d282a2ee2ec24b4230da1e03464ca98
1 /*
2 * $Id: fcgi_util.c,v 1.18 2001/05/03 21:57:35 robs Exp $
3 */
5 #include "fcgi.h"
7 #ifdef WIN32
8 #pragma warning( disable : 4100 )
9 #endif
11 /*******************************************************************************
12 * Compute printable MD5 hash. Pool p is used for scratch as well as for
13 * allocating the hash - use temp storage, and dup it if you need to keep it.
15 char *
16 fcgi_util_socket_hash_filename(pool *p, const char *path,
17 const char *user, const char *group)
19 char *buf = ap_pstrcat(p, path, user, group, NULL);
21 /* Canonicalize the path (remove "//", ".", "..") */
22 ap_getparents(buf);
24 return ap_md5(p, (unsigned char *)buf);
27 /*******************************************************************************
28 * Return absolute path to file in either "regular" FCGI socket directory or
29 * the dynamic directory. Result is allocated in pool p.
31 const char *
32 fcgi_util_socket_make_path_absolute(pool * const p,
33 const char *const file, const int dynamic)
35 if (ap_os_is_path_absolute(file))
37 return file;
39 else
41 const char * parent_dir = dynamic ? fcgi_dynamic_dir : fcgi_socket_dir;
42 return (const char *) ap_make_full_path(p, parent_dir, file);
46 #ifndef WIN32
47 /*******************************************************************************
48 * Build a Domain Socket Address structure, and calculate its size.
49 * The error message is allocated from the pool p. If you don't want the
50 * struct sockaddr_un also allocated from p, pass it preallocated (!=NULL).
52 const char *
53 fcgi_util_socket_make_domain_addr(pool *p, struct sockaddr_un **socket_addr,
54 int *socket_addr_len, const char *socket_path)
56 int socket_pathLen = strlen(socket_path);
58 if (socket_pathLen >= sizeof((*socket_addr)->sun_path)) {
59 return ap_pstrcat(p, "path \"", socket_path,
60 "\" is too long for a Domain socket", NULL);
63 if (*socket_addr == NULL)
64 *socket_addr = ap_pcalloc(p, sizeof(struct sockaddr_un));
65 else
66 memset(*socket_addr, 0, sizeof(struct sockaddr_un));
68 (*socket_addr)->sun_family = AF_UNIX;
69 strcpy((*socket_addr)->sun_path, socket_path);
71 *socket_addr_len = SUN_LEN(*socket_addr);
72 return NULL;
74 #endif
76 /*******************************************************************************
77 * Convert a hostname or IP address string to an in_addr struct.
79 static int
80 convert_string_to_in_addr(const char * const hostname, struct in_addr * const addr)
82 struct hostent *hp;
83 int count;
85 addr->s_addr = inet_addr((char *)hostname);
87 if (addr->s_addr == INADDR_NONE) {
88 if ((hp = gethostbyname((char *)hostname)) == NULL)
89 return -1;
91 memcpy((char *) addr, hp->h_addr, hp->h_length);
92 count = 0;
93 while (hp->h_addr_list[count] != 0)
94 count++;
96 return count;
98 return 1;
102 /*******************************************************************************
103 * Build an Inet Socket Address structure, and calculate its size.
104 * The error message is allocated from the pool p. If you don't want the
105 * struct sockaddr_in also allocated from p, pass it preallocated (!=NULL).
107 const char *
108 fcgi_util_socket_make_inet_addr(pool *p, struct sockaddr_in **socket_addr,
109 int *socket_addr_len, const char *host, unsigned short port)
111 if (*socket_addr == NULL)
112 *socket_addr = ap_pcalloc(p, sizeof(struct sockaddr_in));
113 else
114 memset(*socket_addr, 0, sizeof(struct sockaddr_in));
116 (*socket_addr)->sin_family = AF_INET;
117 (*socket_addr)->sin_port = htons(port);
119 /* Get an in_addr represention of the host */
120 if (host != NULL) {
121 if (convert_string_to_in_addr(host, &(*socket_addr)->sin_addr) != 1) {
122 return ap_pstrcat(p, "failed to resolve \"", host,
123 "\" to exactly one IP address", NULL);
125 } else {
126 (*socket_addr)->sin_addr.s_addr = htonl(INADDR_ANY);
129 *socket_addr_len = sizeof(struct sockaddr_in);
130 return NULL;
133 /*******************************************************************************
134 * Determine if a process with uid/gid can access a file with mode permissions.
136 const char *
137 fcgi_util_check_access(pool *tp,
138 const char * const path, const struct stat *statBuf,
139 const int mode, const uid_t uid, const gid_t gid)
141 if (statBuf == NULL) {
142 static struct stat staticStatBuf;
144 if (stat(path, &staticStatBuf) < 0)
145 return ap_psprintf(tp, "stat() failed: %s", strerror(errno));
146 statBuf = &staticStatBuf;
149 #ifndef WIN32
150 /* If the uid owns the file, check the owner bits */
151 if (uid == statBuf->st_uid) {
152 if (mode & R_OK && !(statBuf->st_mode & S_IRUSR))
153 return "read not allowed by owner";
154 if (mode & W_OK && !(statBuf->st_mode & S_IWUSR))
155 return "write not allowed by owner";
156 if (mode & X_OK && !(statBuf->st_mode & S_IXUSR))
157 return "execute not allowed by owner";
158 return NULL;
160 #else
161 if (mode & _S_IREAD && !(statBuf->st_mode & _S_IREAD))
162 return "read not allowed";
163 if (mode & _S_IWRITE && !(statBuf->st_mode & _S_IWRITE))
164 return "write not allowed";
166 // I don't think this works on FAT, but since I don't know how to check..
167 // if (mode & _S_IEXEC && !(statBuf->st_mode & _S_IEXEC))
168 // return "execute not allowed";
169 #endif
171 #if !defined(__EMX__) && !defined(WIN32)
172 /* If the gid is same as the file's group, check the group bits */
173 if (gid == statBuf->st_gid) {
174 if (mode & R_OK && !(statBuf->st_mode & S_IRGRP))
175 return "read not allowed by group";
176 if (mode & W_OK && !(statBuf->st_mode & S_IWGRP))
177 return "write not allowed by group";
178 if (mode & X_OK && !(statBuf->st_mode & S_IXGRP))
179 return "execute not allowed by group";
180 return NULL;
183 /* Get the user membership for the file's group. If the
184 * uid is a member, check the group bits. */
186 const struct group * const gr = getgrgid(statBuf->st_gid);
187 const struct passwd * const pw = getpwuid(uid);
189 if (gr != NULL && pw != NULL) {
190 char **user = gr->gr_mem;
191 for ( ; *user != NULL; user++) {
192 if (strcmp(*user, pw->pw_name) == 0) {
193 if (mode & R_OK && !(statBuf->st_mode & S_IRGRP))
194 return "read not allowed by group";
195 if (mode & W_OK && !(statBuf->st_mode & S_IWGRP))
196 return "write not allowed by group";
197 if (mode & X_OK && !(statBuf->st_mode & S_IXGRP))
198 return "execute not allowed by group";
199 return NULL;
205 /* That just leaves the other bits.. */
206 if (mode & R_OK && !(statBuf->st_mode & S_IROTH))
207 return "read not allowed";
208 if (mode & W_OK && !(statBuf->st_mode & S_IWOTH))
209 return "write not allowed";
210 if (mode & X_OK && !(statBuf->st_mode & S_IXOTH))
211 return "execute not allowed";
212 #endif
214 return NULL;
218 /*******************************************************************************
219 * Find a FastCGI server with a matching fs_path, and if fcgi_wrapper is
220 * enabled with matching uid and gid.
222 fcgi_server *
223 fcgi_util_fs_get_by_id(const char *ePath, uid_t uid, gid_t gid)
225 char path[FCGI_MAXPATH];
226 fcgi_server *s;
228 /* @@@ This should now be done in the loop below */
229 ap_cpystrn(path, ePath, FCGI_MAXPATH);
230 ap_no2slash(path);
232 for (s = fcgi_servers; s != NULL; s = s->next) {
233 int i;
234 const char *fs_path = s->fs_path;
235 for (i = 0; fs_path[i] && path[i]; ++i) {
236 if (fs_path[i] != path[i]) {
237 break;
240 if (fs_path[i]) {
241 continue;
243 if (path[i] == '\0' || path[i] == '/') {
244 if (fcgi_wrapper == NULL || (uid == s->uid && gid == s->gid))
245 return s;
248 return NULL;
251 /*******************************************************************************
252 * Find a FastCGI server with a matching fs_path, and if fcgi_wrapper is
253 * enabled with matching user and group.
255 fcgi_server *
256 fcgi_util_fs_get(const char *ePath, const char *user, const char *group)
258 char path[FCGI_MAXPATH];
259 fcgi_server *s;
261 ap_cpystrn(path, ePath, FCGI_MAXPATH);
262 ap_no2slash(path);
264 for (s = fcgi_servers; s != NULL; s = s->next) {
265 if (strcmp(s->fs_path, path) == 0) {
266 if (fcgi_wrapper == NULL)
267 return s;
269 if (strcmp(user, s->user) == 0
270 && (user[0] == '~' || strcmp(group, s->group) == 0))
272 return s;
276 return NULL;
279 const char *
280 fcgi_util_fs_is_path_ok(pool * const p, const char * const fs_path, struct stat *finfo)
282 const char *err;
284 if (finfo == NULL) {
285 finfo = (struct stat *)ap_palloc(p, sizeof(struct stat));
286 if (stat(fs_path, finfo) < 0)
287 return ap_psprintf(p, "stat() failed: %s", strerror(errno));
290 /* No Parse Header scripts aren't allowed.
291 * @@@ Well... we really could quite easily */
292 if (strncmp(strrchr(fs_path, '/'), "/nph-", 5) == 0)
293 return ap_psprintf(p, "NPH scripts cannot be run as FastCGI");
295 if (finfo->st_mode == 0)
296 return ap_psprintf(p, "script not found or unable to stat()");
298 if (S_ISDIR(finfo->st_mode))
299 return ap_psprintf(p, "script is a directory!");
301 /* Let the wrapper determine what it can and can't execute */
302 if (! fcgi_wrapper)
304 #ifdef WIN32
305 err = fcgi_util_check_access(p, fs_path, finfo, _S_IEXEC, fcgi_user_id, fcgi_group_id);
306 #else
307 err = fcgi_util_check_access(p, fs_path, finfo, X_OK, fcgi_user_id, fcgi_group_id);
308 #endif
309 if (err) {
310 return ap_psprintf(p,
311 "access for server (uid %ld, gid %ld) not allowed: %s",
312 (long)fcgi_user_id, (long)fcgi_group_id, err);
316 return NULL;
321 /*******************************************************************************
322 * Allocate a new FastCGI server record from pool p with default values.
324 fcgi_server *
325 fcgi_util_fs_new(pool *p)
327 fcgi_server *s = (fcgi_server *) ap_pcalloc(p, sizeof(fcgi_server));
329 /* Initialize anything who's init state is not zeroizzzzed */
330 s->listenQueueDepth = FCGI_DEFAULT_LISTEN_Q;
331 s->appConnectTimeout = FCGI_DEFAULT_APP_CONN_TIMEOUT;
332 s->idle_timeout = FCGI_DEFAULT_IDLE_TIMEOUT;
333 s->initStartDelay = DEFAULT_INIT_START_DELAY;
334 s->restartDelay = FCGI_DEFAULT_RESTART_DELAY;
335 s->restartOnExit = FALSE;
336 s->directive = APP_CLASS_UNKNOWN;
337 s->processPriority = FCGI_DEFAULT_PRIORITY;
338 s->envp = &fcgi_empty_env;
340 #ifdef WIN32
341 s->listenFd = (int) INVALID_HANDLE_VALUE;
342 #else
343 s->listenFd = -2;
344 #endif
346 return s;
349 /*******************************************************************************
350 * Add the server to the linked list of FastCGI servers.
352 void
353 fcgi_util_fs_add(fcgi_server *s)
355 s->next = fcgi_servers;
356 fcgi_servers = s;
359 /*******************************************************************************
360 * Configure uid, gid, user, group, username for wrapper.
362 const char *
363 fcgi_util_fs_set_uid_n_gid(pool *p, fcgi_server *s, uid_t uid, gid_t gid)
365 #ifndef WIN32
366 struct passwd *pw;
367 struct group *gr;
368 #endif
370 if (fcgi_wrapper == NULL)
371 return NULL;
373 #ifndef WIN32
374 s->uid = uid;
375 pw = getpwuid(uid);
376 if (pw == NULL) {
377 return ap_psprintf(p,
378 "getpwuid() couldn't determine the username for uid '%ld', "
379 "you probably need to modify the User directive: %s",
380 (long)uid, strerror(errno));
382 s->user = ap_pstrdup(p, pw->pw_name);
383 s->username = s->user;
385 s->gid = gid;
386 gr = getgrgid(gid);
387 if (gr == NULL) {
388 return ap_psprintf(p,
389 "getgrgid() couldn't determine the group name for gid '%ld', "
390 "you probably need to modify the Group directive: %s",
391 (long)gid, strerror(errno));
393 s->group = ap_pstrdup(p, gr->gr_name);
394 #endif
395 return NULL;
398 /*******************************************************************************
399 * Allocate an array of ServerProcess records.
401 ServerProcess *
402 fcgi_util_fs_create_procs(pool *p, int num)
404 int i;
405 ServerProcess *proc = (ServerProcess *)ap_pcalloc(p, sizeof(ServerProcess) * num);
407 for (i = 0; i < num; i++) {
408 #ifdef WIN32
409 proc[i].handle = INVALID_HANDLE_VALUE;
410 proc[i].terminationEvent = INVALID_HANDLE_VALUE;
411 #endif
412 proc[i].pid = 0;
413 proc[i].state = FCGI_READY_STATE;
415 return proc;
418 int fcgi_util_gettimeofday(struct timeval *Time) {
419 #ifdef WIN32
420 DWORD clock;
421 time_t t;
423 clock = GetTickCount();
425 t = time(NULL);
427 Time->tv_sec = t; //clock / 1000;
428 Time->tv_usec = (clock - Time->tv_sec) * 1000;
430 if (Time->tv_sec == (time_t)-1)
431 return -1;
432 else
433 return 0;
434 #else
435 return gettimeofday(Time, NULL);
436 #endif