2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
11 * Implements the weblog binary which links with the AFS libraries and acts
12 * as the server for authenticating users for apache access to AFS. Code
13 * structure is based on klog.c. The communication with clients is done
14 * via pipes whose file descriptors are passed as command line arguments
15 * thus making it necessary for a common parent to start this process and
16 * the processes that will communicate with it for them to inherit the
17 * pipes. Also passed as a command line argument is a Silent flag (like klog)
18 * and a cache expiration flag which allows cache expiration times for
19 * tokens to be set for testing purposes
23 /* These two needed for rxgen output to work */
24 #include <afsconfig.h>
25 #include <afs/param.h>
29 #include <sys/types.h>
30 #include <sys/errno.h>
43 #include <afs/com_err.h>
45 #include <afs/cellconfig.h>
48 #include "weblog_errors.h"
51 #define MAX(A,B) ((A)>(B)?(A):(B))
54 #define MIN(A,B) ((A)<(B)?(A):(B))
57 #include "apache_afs_utils.h"
62 #include "apache_afs_cache.h"
64 /* the actual function that does all the work! */
68 static char **zero_argv;
70 /* pipes used for communicating with web server */
71 /* these are passed as command line args - defaults to stdin/stdout */
77 * now I know why this was necessary! - it's a hokie thing -
78 * the call to ka_UserAuthenticateGeneral doesn't compile otherwise
87 main(int argc, char **argv)
89 struct cmd_syndesc *ts;
94 * The following signal action for AIX is necessary so that in case of a
95 * crash (i.e. core is generated) we can include the user's data section
96 * in the core dump. Unfortunately, by default, only a partial core is
97 * generated which, in many cases, isn't too useful.
101 sigemptyset(&nsa.sa_mask);
102 nsa.sa_handler = SIG_DFL;
103 nsa.sa_flags = SA_FULLDUMP;
104 sigaction(SIGABRT, &nsa, NULL);
105 sigaction(SIGSEGV, &nsa, NULL);
109 * we ignore SIGPIPE so that EPIPE is returned if there is no one reading
110 * data being written to the pipe
114 /* TODO - for AIX? */
117 sa.sa_handler = SIG_IGN;
118 sigaction(SIGPIPE, &sa, NULL);
124 ts = cmd_CreateSyntax(NULL, CommandProc, NULL,
125 "obtain Kerberos authentication for web servers");
127 /* define the command line arguments */
130 #define aCACHEEXPIRATION 2
131 #define aTOKENEXPIRATION 3
134 cmd_AddParm(ts, "-readPipe", CMD_SINGLE, CMD_OPTIONAL, "inPipefd");
135 cmd_AddParm(ts, "-writePipe", CMD_SINGLE, CMD_OPTIONAL, "outPipefd");
136 cmd_AddParm(ts, "-cacheExpiration", CMD_SINGLE, CMD_OPTIONAL,
137 "local cache expiration times for tokens");
138 cmd_AddParm(ts, "-tokenExpiration", CMD_SINGLE, CMD_OPTIONAL,
139 "cache manager expiration time for tokens");
140 cmd_AddParm(ts, "-silent", CMD_FLAG, CMD_OPTIONAL, "silent operation");
142 code = cmd_Dispatch(argc, argv);
149 * send a buffer over the pipe
152 sendToken(int len, void *buf)
155 WEBLOGEXIT(NULLARGSERROR);
157 if (write(writePipe, buf, len) != len) {
159 perror("weblog: write to pipe error");
168 * read the incoming buffer from the pipe
172 readFromClient(char *buf)
176 WEBLOGEXIT(NULLARGSERROR);
178 n = read(readPipe, buf, MAXBUFF);
181 perror("weblog: pipe read error");
187 perror("weblog: zero bytes read from pipe");
195 * copies the string spereated by the sep into retbuf and returns the position
196 * from the beginning of the string that this string ends at so you can call
197 * it againword seperated by the sep character and give that value as th start
198 * parameter - used to parse incoming buffer from clients over the pipe
201 * NOTE - the space seperated credentials failed for passwds with spaces, thus
202 * we use newline for seperators instead
205 getNullSepWord(char *buf, char sep, char *retBuf, int start)
208 int len = strlen(buf) - start;
211 if ((buf == NULL) || (retBuf == NULL) || (start < 0)) {
212 fprintf(stderr, "weblog (getWordSep):NULL args\n");
216 while ((buf[start] != sep) && (ret <= len)) {
217 retBuf[ret] = buf[start];
227 * parses the NEWLINE seperated buffer giving the username, passwd and cell
228 * coming over the pipe from the clients and sets the variables accordingly
231 parseBuf(char *buf, char *user, char *pass, char *cell, char *type)
234 int start = 0, ret = 0;
236 if ((buf == NULL) || (user == NULL) || (pass == NULL) || (cell == NULL)
239 fprintf(stderr, "afs_Authenticator:parseBuf-an arg was NULL\n");
243 if ((ret = getNullSepWord(buf, '\n', type, start)) < 0) {
247 if ((ret = getNullSepWord(buf, '\n', user, start)) < 0) {
251 if ((ret = getNullSepWord(buf, '\n', cell, start)) < 0) {
255 if ((ret = getNullSepWord(buf, '\n', pass, start)) < 0) {
263 * Discard any authentication information held in trust by the Cache Manager
264 * for the calling process and all other processes in the same PAG
269 return do_pioctl(NULL, 0, NULL, 0, VIOCUNPAG, NULL, 0);
273 /* we can obtain a PAG by calling this system call */
282 * The main procedure that waits in an infinite loop for data to
283 * arrive through a pipe from the httpds, authenticates the user and
284 * returns a token (or a failure message) over the pipe
287 CommandProc(struct cmd_syndesc *as, void *arock)
289 char name[MAXKTCNAMELEN];
290 char cell[MAXKTCREALMLEN];
292 /* All the constant sizes for these arrays are taken from the code for klog */
294 char cksum[SHA_HASH_BYTES]; /* for sha checksum for caching */
295 afs_int32 expires = 0; /* for cache expiration */
296 afs_int32 cacheExpiration = 0; /* configurable cmd line parameter */
297 afs_int32 testExpires = 0; /* cacheExpiration + current time */
299 int authtype = 0; /* AFS or AFS-DFS Authentication */
301 int shutdown = 0; /* on getting shutdown from the pipe we GO */
302 int gotToken = 0; /* did we get a token from the cache manager */
303 afs_int32 i = 0; /* for getting primary token held by CM */
304 int dosetpag = 1; /* not used */
305 Date lifetime; /* requested ticket lifetime */
306 char tbuffer[MAXBUFF]; /* for pioctl ops + pipe transfers */
307 static char rn[] = "weblog"; /* Routine name */
308 static int Silent = 0; /* Don't want error messages */
309 afs_int32 password_expires = -1;
310 char *reason; /* string describing errors */
311 char type[10]; /* authentication type AFS or DFS */
313 /* blow away command line arguments */
314 for (i = 1; i < zero_argc; i++)
315 memset(zero_argv[i], 0, strlen(zero_argv[i]));
318 /* first determine quiet flag based on -silent switch */
319 Silent = (as->parms[aSILENT].items ? 1 : 0);
324 fprintf(stderr, "%s:ka_Init FAILED\n", rn);
329 /* Parse our arguments. */
330 if (as->parms[aREADPIPE].items)
331 /* there is a file descriptor instead of stdin */
332 readPipe = atoi(as->parms[aREADPIPE].items->data);
336 if (as->parms[aWRITEPIPE].items)
337 /* there is a file descriptor instead of stdout */
338 writePipe = atoi(as->parms[aWRITEPIPE].items->data);
342 if (as->parms[aCACHEEXPIRATION].items)
343 /* set configurable cache expiration time */
344 cacheExpiration = atoi(as->parms[aCACHEEXPIRATION].items->data);
346 if (as->parms[aTOKENEXPIRATION].items)
347 /* set configurable token lifetime */
348 lifetime = atoi(as->parms[aTOKENEXPIRATION].items->data);
351 * Initialize the cache for tokens
356 * discard any tokens held for this PAG -
357 * should we create a seperate PAG for weblog first? makeNewPAG does that
360 fprintf(stderr, "%s:Before MAKENEWPAG\n", rn);
362 fprintf(stderr, "\nWEBLOG: before PAG:\t");
366 fprintf(stderr, "WEBLOG: MakeNewPAG failed\n");
369 fprintf(stderr, "weblog:AFTER do_setpag,PAG:\t");
371 fprintf(stderr, "%s:After MAKENEWPAG\n", rn);
377 fprintf(stderr, "WEBLOG: UNLOG FAILED\n");
383 code = readFromClient(tbuffer);
385 tbuffer[code] = '\0';
386 code = parseBuf(tbuffer, name, passwd, cell, type);
388 fprintf(stderr, "weblog: parseBuf FAILED\n");
389 WEBLOGEXIT(PARSEERROR);
393 fprintf(stderr, "%s: readFromClient FAILED:%d...exiting\n", rn,
396 WEBLOGEXIT(PIPEREADERROR);
399 if (strcasecmp(type, "AFS") == 0) {
407 reason = (char *)malloc(sizeof(tbuffer));
408 sprintf(reason, "weblog: Unknown Authentication type:%s.", type);
412 memset((void *)&tbuffer, 0, sizeof(tbuffer));
414 /* look up local cache */
415 weblog_login_checksum(name, cell, passwd, cksum);
416 code = weblog_login_lookup(name, cell, cksum, &tbuffer[0]);
418 if (!code) { /* local cache lookup failed */
419 /* authenticate user */
422 fprintf(stderr, "WEBLOG GROUPCHECK BEFORE KA_AUTH\n");
428 ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION + 0, name,
429 NULL, cell, passwd, lifetime,
430 &password_expires, 0, &reason);
437 "weblog:Unable to authenticate to AFS because "
445 "WEBLOG:After ka_UserAuthenticateGeneral GroupCheck\n");
448 /* get just the ONE token for this PAG from cache manager */
450 memcpy((void *)&tbuffer[0], (void *)&i, sizeof(afs_int32));
452 do_pioctl(tbuffer, sizeof(afs_int32), tbuffer,
453 sizeof(tbuffer), VIOCGETTOK, NULL, 0);
455 fprintf(stderr, "weblog: failed to get token:%d\n", code);
457 "FAILED TO GET TOKEN FROM CACHE MANAGER\n");
462 hexDump(tbuffer, sizeof(tbuffer));
466 /* put the token in local cache with the expiration date/time */
467 expires = getExpiration(tbuffer);
470 fprintf(stderr, "Error getting expiration time\n");
473 weblog_login_checksum(name, cell, passwd, cksum);
474 if (cacheExpiration == 0) {
475 weblog_login_store(name, cell, cksum, &tbuffer[0],
476 sizeof(tbuffer), expires);
478 testExpires = cacheExpiration + time(NULL);
479 weblog_login_store(name, cell, cksum, &tbuffer[0],
480 sizeof(tbuffer), MIN(expires,
487 /* cache lookup succesful */
489 fprintf(stderr, "WEBLOG: Cache lookup succesful\n");
494 /* prepare the reply buffer with this token */
498 /* respond with a reason why authentication failed */
499 sprintf(tbuffer, "FAILURE:%s", reason);
502 /* send response to client */
503 code = sendToken(sizeof(tbuffer), tbuffer);
506 fprintf(stderr, "sendToken FAILED\n");
508 WEBLOGEXIT(PIPESENDERROR);
510 /* unlog after every request unconditionally */
513 fprintf(stderr, "WEBLOG: UNLOG FAILED\n");