ubik-call-sucks-20060703
[openafs.git] / src / afsweb / weblog.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
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
8  */
9
10 /*
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
20  */
21
22
23 /* These two needed for rxgen output to work */
24 #include <afsconfig.h>
25 #include <afs/param.h>
26
27 RCSID
28     ("$Header$");
29
30 #include <afs/stds.h>
31 #include <sys/types.h>
32 #include <sys/errno.h>
33 #include <rx/xdr.h>
34
35 #ifdef  AFS_AIX32_ENV
36 #include <signal.h>
37 #endif
38
39 #include <lock.h>
40 #include <ubik.h>
41 #include <stdio.h>
42 #include <pwd.h>
43 #include <signal.h>
44
45 #include <afs/com_err.h>
46 #include <afs/auth.h>
47 #include <afs/cellconfig.h>
48 #include <afs/cmd.h>
49
50 #include "weblog_errors.h"
51
52 #ifndef MAX
53 #define MAX(A,B)                ((A)>(B)?(A):(B))
54 #endif /* !MAX */
55 #ifndef MIN
56 #define MIN(A,B)                ((A)<(B)?(A):(B))
57 #endif /* !MAX */
58
59 #include "apache_afs_utils.h"
60
61 #define MAXBUFF 1024
62
63 /* For caching */
64 #include "apache_afs_cache.h"
65
66 /* the actual function that does all the work! */
67 int CommandProc();
68
69 static int zero_argc;
70 static char **zero_argv;
71
72 /* pipes used for communicating with web server */
73 /* these are passed as command line args - defaults to stdin/stdout */
74
75 static int readPipe;
76 static int writePipe;
77
78 /* 
79  * now I know why this was necessary! - it's a hokie thing - 
80  * the call to ka_UserAuthenticateGeneral doesn't compile otherwise
81  */
82 int
83 osi_audit()
84 {
85     return 0;
86 }
87
88
89 main(int argc, char **argv)
90 {
91     struct cmd_syndesc *ts;
92     afs_int32 code;
93
94 #ifdef  AFS_AIX32_ENV
95     /*
96      * The following signal action for AIX is necessary so that in case of a 
97      * crash (i.e. core is generated) we can include the user's data section 
98      * in the core dump. Unfortunately, by default, only a partial core is
99      * generated which, in many cases, isn't too useful.
100      */
101     struct sigaction nsa;
102
103     sigemptyset(&nsa.sa_mask);
104     nsa.sa_handler = SIG_DFL;
105     nsa.sa_flags = SA_FULLDUMP;
106     sigaction(SIGABRT, &nsa, NULL);
107     sigaction(SIGSEGV, &nsa, NULL);
108 #endif
109
110 /* 
111  * we ignore SIGPIPE so that EPIPE is returned if there is no one reading
112  * data being written to the pipe 
113  */
114
115 #ifdef AIX
116     /* TODO - for AIX? */
117 #else
118     struct sigaction sa;
119     sa.sa_handler = SIG_IGN;
120     sigaction(SIGPIPE, &sa, NULL);
121 #endif
122
123     zero_argc = argc;
124     zero_argv = argv;
125
126     ts = cmd_CreateSyntax(NULL, CommandProc, 0,
127                           "obtain Kerberos authentication for web servers");
128
129 /* define the command line arguments */
130 #define aREADPIPE 0
131 #define aWRITEPIPE 1
132 #define aCACHEEXPIRATION 2
133 #define aTOKENEXPIRATION 3
134 #define aSILENT 4
135
136     cmd_AddParm(ts, "-readPipe", CMD_SINGLE, CMD_OPTIONAL, "inPipefd");
137     cmd_AddParm(ts, "-writePipe", CMD_SINGLE, CMD_OPTIONAL, "outPipefd");
138     cmd_AddParm(ts, "-cacheExpiration", CMD_SINGLE, CMD_OPTIONAL,
139                 "local cache expiration times for tokens");
140     cmd_AddParm(ts, "-tokenExpiration", CMD_SINGLE, CMD_OPTIONAL,
141                 "cache manager expiration time for tokens");
142     cmd_AddParm(ts, "-silent", CMD_FLAG, CMD_OPTIONAL, "silent operation");
143
144     code = cmd_Dispatch(argc, argv);
145
146     WEBLOGEXIT(code);
147 }
148
149
150 /*
151  * send a buffer over the pipe 
152  */
153 static int
154 sendToken(int len, void *buf)
155 {
156     if (buf == NULL) {
157         WEBLOGEXIT(NULLARGSERROR);
158     }
159     if (write(writePipe, buf, len) != len) {
160 #ifdef DEBUG
161         perror("weblog: write to pipe error");
162 #endif
163         return -1;
164     }
165     return 0;
166 }
167
168
169 /*
170  * read the incoming buffer from the pipe
171  */
172
173 static int
174 readFromClient(char *buf)
175 {
176     int n;
177     if (buf == NULL) {
178         WEBLOGEXIT(NULLARGSERROR);
179     }
180     n = read(readPipe, buf, MAXBUFF);
181     if (n < 0) {
182 #ifdef DEBUG
183         perror("weblog: pipe read error");
184 #endif
185         return -1;
186     }
187     if (n == 0) {
188 #ifdef DEBUG
189         perror("weblog: zero bytes read from pipe");
190 #endif
191         return -1;
192     }
193     return n;
194 }
195
196 /* 
197  * copies the string spereated by the sep into retbuf and returns the position
198  * from the beginning of the string that this string ends at so you can call 
199  * it againword seperated by the sep character and give that value as th start 
200  * parameter - used to parse incoming buffer from clients over the pipe
201  */
202 /* 
203  * NOTE - the space seperated credentials failed for passwds with spaces, thus
204  * we use newline for seperators instead
205  */
206 static int
207 getNullSepWord(char *buf, char sep, char *retBuf, int start)
208 {
209     int ret = 0;
210     int len = strlen(buf) - start;
211
212     /* error checks */
213     if ((buf == NULL) || (retBuf == NULL) || (start < 0)) {
214         fprintf(stderr, "weblog (getWordSep):NULL args\n");
215         return -1;
216     }
217
218     while ((buf[start] != sep) && (ret <= len)) {
219         retBuf[ret] = buf[start];
220         ret++;
221         start++;
222     }
223     retBuf[ret] = '\0';
224     return (ret + 1);
225 }
226
227
228 /* 
229  * parses the NEWLINE seperated buffer giving the username, passwd and cell
230  * coming over the pipe from the clients and sets the variables accordingly
231  */
232 static int
233 parseBuf(char *buf, char *user, char *pass, char *cell, char *type)
234 {
235     char *temp;
236     int start = 0, ret = 0;
237
238     if ((buf == NULL) || (user == NULL) || (pass == NULL) || (cell == NULL)
239         || (type == NULL)) {
240 #ifdef DEBUG
241         fprintf(stderr, "afs_Authenticator:parseBuf-an arg was NULL\n");
242 #endif
243         return -1;
244     }
245     if ((ret = getNullSepWord(buf, '\n', type, start)) < 0) {
246         return -1;
247     }
248     start += ret;
249     if ((ret = getNullSepWord(buf, '\n', user, start)) < 0) {
250         return -1;
251     }
252     start += ret;
253     if ((ret = getNullSepWord(buf, '\n', cell, start)) < 0) {
254         return -1;
255     }
256     start += ret;
257     if ((ret = getNullSepWord(buf, '\n', pass, start)) < 0) {
258         return -1;
259     }
260     return 0;
261 }
262
263
264 /*
265  * Discard any authentication information held in trust by the Cache Manager
266  * for the calling process and all other processes in the same PAG
267  */
268 static int
269 unlog()
270 {
271     return do_pioctl(NULL, 0, NULL, 0, VIOCUNPAG, NULL, 0);
272 }
273
274
275 /* we can obtain a PAG by calling this system call */
276 static int
277 makeNewPAG()
278 {
279     return do_setpag();
280 }
281
282 #ifdef ENABLE_DCE_DLOG
283 /*
284  * Attempt to use the dlog mechanism to get a DCE-DFS ticket into the AFS cache manager
285  */
286
287 #include "adkint.h"
288 #include "assert.h"
289 #include <des.h>
290 #include <afs/afsutil.h>
291
292 /*
293  * The un-decoded version of the encrypted portion of the kdc
294  * AS reply message (see Kerberos V5 spec).
295  */
296 typedef struct kdc_as_reply {
297     des_cblock session_key;
298     afs_int32 nonce;
299     afs_int32 authtime;
300     afs_int32 starttime;
301     afs_int32 endtime;
302     char *realm;
303 } kdc_as_reply_t;
304
305 static char *
306 makeString(char *sp)
307 {
308     int len;
309     char *new_string;
310
311     if (sp == NULL) {
312         fprintf(stderr, "weblog: makeString - NULL argument\n");
313         return NULL;
314     }
315     len = strlen(sp);
316     if (len < 0) {
317         fprintf(stderr, "weblog: makeString. strlen error\n");
318         return NULL;
319     }
320     new_string = (char *)malloc(len + 1);
321     if (new_string == NULL) {
322         fprintf(stderr, "weblog: Out of memory - malloc failed\n");
323         return NULL;
324     }
325     strncpy(new_string, sp, len);
326     return new_string;
327 }
328
329 /*
330  * Store the returned credentials as an AFS "token" for the user
331  * "AFS ID <user_id>".
332  */
333 static int
334 store_afs_token(unix_id, realm_p, tkt_type, ticket_p, ticket_len, session_key,
335                 starttime, endtime, set_pag)
336      afs_int32 unix_id;
337      char *realm_p;
338      afs_int32 tkt_type;
339      unsigned char *ticket_p;
340      int ticket_len;
341      des_cblock session_key;
342      afs_int32 starttime;
343      afs_int32 endtime;
344      int set_pag;
345 {
346     struct ktc_token token;
347     struct ktc_principal client, server;
348
349     token.startTime = starttime;
350     token.endTime = endtime;
351     memcpy((char *)&token.sessionKey, session_key, sizeof(token.sessionKey));
352     token.kvno = tkt_type;
353     token.ticketLen = ticket_len;
354     if (ticket_len > MAXKTCTICKETLEN) {
355         fprintf(stderr,
356                 "weblog: DCE ticket is too long (length %d)."
357                 "Maximum length accepted by AFS cache manager is %d\n",
358                 MAXKTCTICKETLEN);
359         return -1;
360     }
361     memcpy((char *)token.ticket, (char *)ticket_p, ticket_len);
362
363     sprintf(client.name, "AFS ID %d", unix_id);
364     strcpy(client.instance, "");
365     strcpy(client.cell, realm_p);
366
367     strcpy(server.name, "afs");
368     strcpy(server.instance, "");
369     strcpy(server.cell, realm_p);
370
371     return (ktc_SetToken
372             (&server, &token, &client, set_pag ? AFS_SETTOK_SETPAG : 0));
373 }
374
375
376 static char *
377 make_string(s_p, length)
378      char *s_p;
379      int length;
380 {
381     char *new_p = (char *)malloc(length + 1);
382     if (new_p == NULL) {
383         fprintf(stderr, "dlog: out of memory\n");
384         exit(1);
385     }
386     memcpy(new_p, s_p, length);
387     new_p[length] = '\0';
388     return new_p;
389 }
390
391 /*
392  * Decode an asn.1 GeneralizedTime, turning it into a 32-bit Unix time.
393  * Format is fixed at YYYYMMDDHHMMSS plus a terminating "Z".
394  *
395  * NOTE: A test for this procedure is included at the end of this file.
396  */
397 static int
398 decode_asn_time(buf, buflen, utime)
399      char *buf;
400      int buflen;
401      afs_int32 *utime;
402 {
403     int year, month, day, hour, mina, sec;
404     int leapyear, days;
405     static mdays[11] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30 };
406     int m;
407
408     if (buflen != 15
409         || sscanf(buf, "%4d%2d%2d%2d%2d%2dZ", &year, &month, &day, &hour,
410                   &mina, &sec) != 6) {
411         return 1;
412     }
413     leapyear = month > 2 ? (year + 1) : year;   /* Account for feb 29 if
414                                                  * current year is a leap year */
415     for (days = 0, m = 0; m < month - 1; m++)
416         days += mdays[m];
417
418     *utime =
419         ((((((year - 1970) * 365 + (leapyear - 1970 + 1) / 4 + days + day -
420              1) * 24) + hour) * 60 + mina) * 60) + sec;
421     return 0;
422 }
423
424 /*
425  * A quick and (very) dirty ASN.1 decode of the ciphertext portion
426  * of the KDC AS reply message... good enough for a product with a short
427  * expected lifetime (this will work as long as the message format doesn't
428  * change much).
429  *
430  * Assumptions:
431  * 
432  * 1. The nonce is the only INTEGER with a tag of [2], at any nesting level.
433  * 2. The session key is the only OCTET STRING with length 8 and tag [1].
434  * 3. The authtime, starttime, and endtimes are the only Generalized Time
435  *    strings with tags 5, 6, and 7, respectively.
436  * 4. The realm is the only General String with tag 9.
437  * 5. The tags, above, are presented in ascending order.
438  */
439
440 #define ASN_INTEGER             0x2
441 #define ASN_OCTET_STRING        0x4
442 #define ASN_TIME                0x18
443 #define ASN_GENERAL_STRING      0x1b
444 #define KDC_REP                 0x7a
445
446 static int
447 decode_reply(buf, buflen, reply_p)
448      unsigned char *buf;        /* encoded ASN.1 string */
449      int buflen;                /* length of encoded string */
450      kdc_as_reply_t *reply_p;   /* result */
451 {
452     unsigned char *limit = buf + buflen;
453
454     char saw_nonce = 0;
455     char saw_kdc_rep = 0;
456     char saw_session_key = 0;
457     char saw_authtime = 0;
458     char saw_starttime = 0;
459     char saw_endtime = 0;
460     char saw_realm = 0;
461
462     int context = -1;           /* Initialize with invalid context */
463
464     reply_p->starttime = 0;     /* This is optionally provided by kdc */
465
466     while (buf < limit) {
467         int op;
468         int len;
469
470         op = *buf++;
471         len = *buf++;
472         if ((op & 0x20) == 0) {
473             /* Primitive encoding */
474             if (len & 0x80) {
475                 return 1;       /* Forget about long unspecified lengths */
476             }
477             /* Bounds check */
478             if (buf + len > limit)
479                 return 1;
480         }
481
482         switch (op) {
483         case KDC_REP:
484             saw_kdc_rep++;
485             break;
486
487         case ASN_INTEGER:
488             {
489                 /*
490                  * Since non ANSI C doesn't recognize the "signed"
491                  * type attribute for chars, we have to fiddle about
492                  * to get sign extension (the sign bit is the top bit
493                  * of the first byte).
494                  */
495 #define         SHIFTSIGN ((sizeof(afs_int32) - 1) * 8)
496                 afs_int32 val;
497                 val = (afs_int32) (*buf++ << SHIFTSIGN) >> SHIFTSIGN;
498
499                 while (--len)
500                     val = (val << 8) | *buf++;
501
502                 if (context == 2) {
503                     reply_p->nonce = val;
504                     saw_nonce++;
505                 }
506             }
507             break;
508
509         case ASN_OCTET_STRING:
510             if (context == 1 && len == sizeof(reply_p->session_key)) {
511                 saw_session_key++;
512                 memcpy(reply_p->session_key, buf, len);
513             }
514             buf += len;
515             break;
516
517         case ASN_GENERAL_STRING:
518             if (context == 9) {
519                 saw_realm = 1;
520                 reply_p->realm = make_string(buf, len);
521                 goto out;       /* Best to terminate now, rather than
522                                  * continue--we don't know if the entire
523                                  * request is padded with zeroes, and if
524                                  * not there is a danger of misinterpreting
525                                  * an op-code (since the request may well
526                                  * be padded somewhat, for encryption purposes)
527                                  * This would work much better if we really
528                                  * tracked constructed type boundaries.
529                                  */
530             }
531             buf += len;
532             break;
533
534         case ASN_TIME:
535             switch (context) {
536             case 5:
537                 saw_authtime++;
538                 if (decode_asn_time(buf, len, &reply_p->authtime))
539                     return 1;
540                 break;
541
542             case 6:
543                 saw_starttime++;
544                 if (decode_asn_time(buf, len, &reply_p->starttime))
545                     return 1;
546                 break;
547
548             case 7:
549                 saw_endtime++;
550                 if (decode_asn_time(buf, len, &reply_p->endtime))
551                     return 1;
552                 break;
553             }
554             buf += len;
555             break;
556
557         default:
558             if ((op & 0xe0) == 0xa0) {
559                 /* Remember last context label */
560                 context = op & 0x1f;
561             } else if ((op & 0x20) == 0) {
562                 /* Skip primitive encodings we don't understand */
563                 buf += len;
564             }
565         }
566     }
567
568   out:
569     return !(saw_kdc_rep == 1 && saw_nonce == 1 && saw_session_key == 1
570              && saw_authtime == 1 && (saw_starttime == 1
571                                       || saw_starttime == 0)
572              && saw_endtime == 1 && saw_realm == 1);
573 }
574
575
576 /*
577  * Attempt to obtain a DFS ticket 
578  */
579 static int
580 getDFScreds(char *name, char *realm, char *passwd, afs_uint32 lifetime,
581             char **reason)
582 {
583     extern ADK_GetTicket();
584     afs_int32 serverList[MAXSERVERS];
585     struct rx_connection *serverconns[MAXSERVERS];
586     struct ubik_client *ubik_handle = 0;
587     struct timeval now;         /* current time */
588     afs_int32 nonce;            /* Kerberos V5 "nonce" */
589     adk_error_ptr error_p;      /* Error code from ktc intermediary */
590     adk_reply_ptr reply_p;      /* reply from ktc intermediary */
591     des_cblock passwd_key;      /* des key from user password */
592     des_key_schedule schedule;  /* Key schedule from key */
593     kdc_as_reply_t kdcrep;      /* Our own decoded version of 
594                                  * ciphertext portion of kdc reply */
595     int code;
596     struct afsconf_dir *cdir;   /* Open configuration structure */
597     int i;
598     struct afsconf_cell cellinfo;       /* Info for specified cell */
599
600     if ((name == NULL) || (realm == NULL) || (passwd == NULL)) {
601         *reason = makeString("weblog: NULL Arguments to getDCEcreds");
602         return -1;
603     }
604
605     cdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH);
606     if (!cdir) {
607         *reason =
608             makeString("weblog: unable to read or open AFS client "
609                        "configuration  file");
610         return -1;
611     }
612
613     /*
614      * Resolve full name of cell and get server list.
615      */
616     code = afsconf_GetCellInfo(cdir, realm, 0, &cellinfo);
617     if (code) {
618         *reason = makeString("-- unable to get cell info");
619         return -1;
620     }
621
622     if (strcmp(realm, cellinfo.name)) {
623         strncpy(realm, cellinfo.name, sizeof(realm) - 1);
624         realm[sizeof(realm) - 1] = '\0';
625     }
626
627     for (i = 0; i < cellinfo.numServers && i < MAXSERVERS; i++) {
628         serverList[i] = cellinfo.hostAddr[i].sin_addr.s_addr;
629     }
630     if (i < MAXSERVERS)
631         serverList[i] = 0;
632
633     /*
634      * Make connections to all the servers.
635      */
636     rx_Init(0);
637     for (i = 0; i < MAXSERVERS; i++) {
638         if (!serverList[i]) {
639             serverconns[i] = 0;
640             break;
641         }
642         serverconns[i] =
643             rx_NewConnection(serverList[i], htons(ADK_PORT), ADK_SERVICE,
644                              rxnull_NewClientSecurityObject(), 0);
645     }
646
647     /*
648      * Initialize ubik client gizmo to randomize the calls for us.
649      */
650     ubik_ClientInit(serverconns, &ubik_handle);
651     /*
652      * Come up with an acceptable nonce. V5 doc says that we just need
653      * time of day. Actually, for this app, I don't think anything
654      * is really needed. Better safe than sorry (although I wonder if
655      * it might have been better to encode the AFS ID in the nonce
656      * reply field--that's the one field that the intermediate server
657      * has total control over, and which can be securely transmitted
658      * back to the client).
659      */
660     gettimeofday(&now, 0);
661     nonce = now.tv_sec;
662
663
664     /*
665      * Ask our agent to get us a Kerberos V5 ticket.
666      */
667     reply_p = (adk_reply_ptr) 0;
668     error_p = (adk_error_ptr) 0;
669     code = ubik_ADK_GetTicket(ubik_handle, 0,   /* Ubik flags */
670                      name,      /* IN:  Principal: must be exact DCE principal */
671                      nonce,     /* IN:  Input nonce */
672                      lifetime,  /* IN:  lifetime */
673                      &error_p,  /* OUT: Error, if any */
674                      &reply_p); /* OUT: KTC reply, if no error */
675
676     /*
677      * Destroy Rx connections on the off-chance this will allow less state
678      * to be preserved at the server.
679      */
680     ubik_ClientDestroy(ubik_handle);
681
682     /*
683      * Finalize Rx. This may allow connections at the server to wind down
684      * faster.
685      */
686     rx_Finalize();
687
688
689     /*
690      * Check for simple communication failures.
691      */
692     if (code) {
693         *reason = makeString("-- failed to contact authentication service");
694         return -1;
695     }
696
697     /*
698      * Also check for DCE errors, which are interpreted for us by
699      * the translator.
700      */
701     if (error_p && error_p->code) {
702         *reason = (char *)makeString(error_p->data);
703         fprintf(stderr, "weblog error:error_p->data:%s\n", error_p->data);
704         return -1;
705     }
706
707
708     /*
709      * Make sure the reply was filled in.
710      */
711     if (!reply_p) {
712         *reason = (char *)
713             makeString
714             ("weblog: unexpected error in server response; aborted");
715         return -1;
716     }
717
718
719     /*
720      * Convert the password into the appropriate key block, given
721      * the salt passed back from the ADK_GetTicket call, above. Destroy
722      * the password.
723      */
724     if (strlen(passwd) + strlen(reply_p->salt) + 1 > BUFSIZ) {
725         *reason = (char *)
726             makeString("weblog: unexpectedly long passwd/salt combination");
727         return -1;
728     }
729     strcat(passwd, reply_p->salt);
730     des_string_to_key(passwd, passwd_key);
731
732     /* Destroy the password. */
733     memset(passwd, 0, strlen(passwd));
734
735
736     /*
737      * Decrypt the private data returned by the DCE KDC, and forwarded
738      * to us by the translator.
739      */
740     code = des_key_sched(passwd_key, schedule);
741     if (!code) {
742         code =
743             des_cbc_encrypt(reply_p->private.adk_code_val,
744                             reply_p->private.adk_code_val,
745                             reply_p->private.adk_code_len, schedule,
746                             passwd_key, DECRYPT);
747     }
748     if (code) {
749         *reason =
750             (char *)makeString("-- unable to decrypt reply from the DCE KDC");
751         return -1;
752     }
753
754     /*
755      * Destroy the key block: it's no longer needed.
756      */
757     memset(schedule, 0, sizeof(schedule));
758     memset(passwd_key, 0, sizeof(passwd_key));
759
760
761     /*
762      * Do a very quick and dirty ASN.1 decode of the relevant parts
763      * of the private data.
764      *
765      * The decrypted data contains a 12-byte header (confounder and CRC-32
766      * checksum). We choose to ignore this.
767      */
768     code = decode_reply(reply_p->private.adk_code_val + 12,     /* Skip header */
769                         reply_p->private.adk_code_len - 12,     /* ditto */
770                         &kdcrep);
771
772     if (code || kdcrep.nonce != nonce) {
773         *reason =
774             (char *)makeString("weblog: DCE authentication failed -- "
775                                "password is probably incorrect");
776         return -1;
777     }
778
779
780     /*
781      * Make an AFS token out of the ticket and session key, and install it
782      * in the cache manager.
783      */
784     code =
785         store_afs_token(reply_p->unix_id, realm, reply_p->tktype,
786                         reply_p->ticket.adk_code_val,
787                         reply_p->ticket.adk_code_len, kdcrep.session_key,
788                         kdcrep.starttime ? kdcrep.starttime : kdcrep.authtime,
789                         kdcrep.endtime, 0);
790
791     if (code) {
792         *reason = (char *)
793             makeString("weblog -- getDCEcreds:failed to store tickets");
794         return -1;
795     }
796     return 0;
797 }
798 #endif /* ENABLE_DCE_DLOG */
799
800
801 /*
802  * The main procedure that waits in an infinite loop for data to
803  * arrive through a pipe from the httpds, authenticates the user and
804  * returns a token (or a failure message) over the pipe
805  */
806 CommandProc(as, arock)
807      char *arock;
808      struct cmd_syndesc *as;
809
810 {
811     char name[MAXKTCNAMELEN];
812     char cell[MAXKTCREALMLEN];
813     char passwd[BUFSIZ];
814 /* All the constant sizes for these arrays are taken from the code for klog */
815
816     char cksum[SHA_HASH_BYTES]; /* for sha checksum for caching */
817     afs_int32 expires = 0;      /* for cache expiration */
818     afs_int32 cacheExpiration = 0;      /* configurable cmd line parameter */
819     afs_int32 testExpires = 0;  /* cacheExpiration + current time */
820
821     int authtype = 0;           /* AFS or AFS-DFS Authentication */
822     int code;
823     int shutdown = 0;           /* on getting shutdown from the pipe we GO */
824     int gotToken = 0;           /* did we get a token from the cache manager */
825     afs_int32 i = 0;            /* for getting primary token held by CM */
826     int dosetpag = 1;           /* not used */
827     Date lifetime;              /* requested ticket lifetime */
828     char tbuffer[MAXBUFF];      /* for pioctl ops + pipe transfers */
829     static char rn[] = "weblog";        /* Routine name */
830     static int Silent = 0;      /* Don't want error messages */
831     afs_int32 password_expires = -1;
832     char *reason;               /* string describing errors */
833     char type[10];              /* authentication type AFS or DFS */
834
835     /* blow away command line arguments */
836     for (i = 1; i < zero_argc; i++)
837         memset(zero_argv[i], 0, strlen(zero_argv[i]));
838     zero_argc = 0;
839
840     /* first determine quiet flag based on -silent switch */
841     Silent = (as->parms[aSILENT].items ? 1 : 0);
842
843     code = ka_Init(0);
844     if (code) {
845         if (!Silent) {
846             fprintf(stderr, "%s:ka_Init FAILED\n", rn);
847         }
848         WEBLOGEXIT(KAERROR);
849     }
850
851     /* Parse our arguments. */
852     if (as->parms[aREADPIPE].items)
853         /* there is a file descriptor instead of stdin */
854         readPipe = atoi(as->parms[aREADPIPE].items->data);
855     else
856         readPipe = stdin;
857
858     if (as->parms[aWRITEPIPE].items)
859         /* there is a file descriptor instead of stdout */
860         writePipe = atoi(as->parms[aWRITEPIPE].items->data);
861     else
862         writePipe = stdout;
863
864     if (as->parms[aCACHEEXPIRATION].items)
865         /* set configurable cache expiration time */
866         cacheExpiration = atoi(as->parms[aCACHEEXPIRATION].items->data);
867
868     if (as->parms[aTOKENEXPIRATION].items)
869         /* set configurable token lifetime */
870         lifetime = atoi(as->parms[aTOKENEXPIRATION].items->data);
871
872     /*
873      * Initialize the cache for tokens
874      */
875     token_cache_init();
876
877     /* 
878      * discard any tokens held for this PAG - 
879      * should we create a seperate PAG for weblog first? makeNewPAG does that
880      */
881 #ifdef DEBUG
882     fprintf(stderr, "%s:Before MAKENEWPAG\n", rn);
883     printGroups();
884     fprintf(stderr, "\nWEBLOG: before PAG:\t");
885     printPAG();
886 #endif
887     if (makeNewPAG()) {
888         fprintf(stderr, "WEBLOG: MakeNewPAG failed\n");
889     }
890 #ifdef DEBUG
891     fprintf(stderr, "weblog:AFTER do_setpag,PAG:\t");
892     printPAG();
893     fprintf(stderr, "%s:After MAKENEWPAG\n", rn);
894     printGroups();
895 #endif
896
897     if (unlog()) {
898 #ifdef DEBUG
899         fprintf(stderr, "WEBLOG: UNLOG FAILED\n");
900 #endif
901     }
902
903     while (!shutdown) {
904         gotToken = 0;
905         code = readFromClient(tbuffer);
906         if (code > 0) {
907             tbuffer[code] = '\0';
908             code = parseBuf(tbuffer, name, passwd, cell, type);
909             if (code) {
910                 fprintf(stderr, "weblog: parseBuf FAILED\n");
911                 WEBLOGEXIT(PARSEERROR);
912             }
913         } else {
914 #ifdef DEBUG
915             fprintf(stderr, "%s: readFromClient FAILED:%d...exiting\n", rn,
916                     code);
917 #endif
918             WEBLOGEXIT(PIPEREADERROR);
919         }
920
921         if (strcasecmp(type, "AFS") == 0) {
922             authtype = 1;
923         }
924 #ifdef ENABLE_DCE_DLOG
925         else if (strcasecmp(type, "AFS-DFS") == 0) {
926             authtype = 2;
927         }
928 #endif /* ENABLE_DCE_DLOG */
929         else {
930             authtype = 0;
931         }
932
933         if (!authtype) {
934             reason = (char *)malloc(sizeof(tbuffer));
935             sprintf(reason,
936                     "weblog: Unknown Authentication type:%s. AFS-DFS login "
937                     "may not be enabled - check compile flags for ENABLE_DCE_DLOG",
938                     type);
939             goto reply_failure;
940         }
941
942         memset((void *)&tbuffer, 0, sizeof(tbuffer));
943
944         /* look up local cache */
945         weblog_login_checksum(name, cell, passwd, cksum);
946         code = weblog_login_lookup(name, cell, cksum, &tbuffer[0]);
947
948         if (!code) {            /* local cache lookup failed */
949             /* authenticate user */
950
951 #ifdef DEBUG
952             fprintf(stderr, "WEBLOG GROUPCHECK BEFORE KA_AUTH\n");
953             printGroups();
954 #endif
955
956             if (authtype == 1) {
957                 code =
958                     ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION + 0, name,
959                                                NULL, cell, passwd, lifetime,
960                                                &password_expires, 0, &reason);
961             }
962 #ifdef ENABLE_DCE_DLOG
963             else if (authtype == 2) {
964                 unlog();
965                 code = getDFScreds(name, cell, passwd, lifetime, &reason);
966             }
967 #ifdef DEBUG_DCE
968             printf("Code:%d\n", code);
969             if (code) {
970                 if (reason) {
971                     printf("FAILURE:Reason:%s\n", reason);
972                 }
973             }
974 #endif
975 #endif /*  ENABLE_DCE_DLOG */
976
977             if (code) {
978 #ifdef DEBUG
979                 if (!Silent) {
980                     fprintf(stderr,
981                             "weblog:Unable to authenticate to AFS because "
982                             "%s\n", reason);
983                 }
984 #endif
985                 goto reply_failure;
986             } else {
987 #ifdef DEBUG
988                 fprintf(stderr,
989                         "WEBLOG:After ka_UserAuthenticateGeneral GroupCheck\n");
990                 printGroups();
991 #endif
992                 /* get just the ONE token for this PAG from cache manager */
993                 i = 0;
994                 memcpy((void *)&tbuffer[0], (void *)&i, sizeof(afs_int32));
995                 code =
996                     do_pioctl(tbuffer, sizeof(afs_int32), tbuffer,
997                               sizeof(tbuffer), VIOCGETTOK, NULL, 0);
998                 if (code) {
999                     fprintf(stderr, "weblog: failed to get token:%d\n", code);
1000                     strcpy(reason,
1001                            "FAILED TO GET TOKEN FROM CACHE MANAGER\n");
1002                 } else {
1003                     gotToken = 1;
1004
1005 #ifdef DEBUG
1006                     hexDump(tbuffer, sizeof(tbuffer));
1007                     parseToken(tbuffer);
1008 #endif /* _DEBUG */
1009
1010                     /* put the token in local cache with the expiration date/time */
1011                     expires = getExpiration(tbuffer);
1012                     if (expires < 0) {
1013 #ifdef DEBUG
1014                         fprintf(stderr, "Error getting expiration time\n");
1015 #endif
1016                     } else {
1017                         weblog_login_checksum(name, cell, passwd, cksum);
1018                         if (cacheExpiration == 0) {
1019                             weblog_login_store(name, cell, cksum, &tbuffer[0],
1020                                                sizeof(tbuffer), expires);
1021                         } else {
1022                             testExpires = cacheExpiration + time(NULL);
1023                             weblog_login_store(name, cell, cksum, &tbuffer[0],
1024                                                sizeof(tbuffer), MIN(expires,
1025                                                                     testExpires));
1026                         }
1027                     }
1028                 }
1029             }
1030         } else {
1031             /* cache lookup succesful */
1032 #ifdef DEBUG
1033             fprintf(stderr, "WEBLOG: Cache lookup succesful\n");
1034 #endif
1035             gotToken = 1;
1036         }
1037
1038         /* prepare the reply buffer with this token */
1039         if (!gotToken) {
1040
1041           reply_failure:
1042             /* respond with a reason why authentication failed */
1043             sprintf(tbuffer, "FAILURE:%s", reason);
1044         }
1045
1046         /* send response to client */
1047         code = sendToken(sizeof(tbuffer), tbuffer);
1048         if (code) {
1049 #ifdef DEBUG
1050             fprintf(stderr, "sendToken FAILED\n");
1051 #endif
1052             WEBLOGEXIT(PIPESENDERROR);
1053         }
1054         /* unlog after every request unconditionally */
1055         if (unlog()) {
1056 #ifdef DEBUG
1057             fprintf(stderr, "WEBLOG: UNLOG FAILED\n");
1058 #endif
1059         }
1060     }                           /* end - while */
1061     return 0;
1062 }