reindent-20030715
[openafs.git] / src / afsweb / apache_afs_cache.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 /* This code borrows from nsafs.c - slightly modified - names,etc. */
11
12 #include <afsconfig.h>
13 #include <afs/param.h>
14
15 RCSID
16     ("$Header$");
17
18 #include "apache_afs_cache.h"
19
20 /*
21  * Value used to initialize SHA checksums on username/password pairs
22  */
23 afs_uint32 weblog_login_pad[SHA_HASH_INTS] = {
24     0x0D544971, 0x2281AC5B, 0x58B51218, 0x4085E08D, 0xB68C484B
25 };
26
27 /*
28  * Cache of AFS logins
29  */
30 struct {
31     struct weblog_login *head;
32     struct weblog_login *tail;
33 } weblog_login_cache[WEBLOG_LOGIN_HASH_SIZE];
34
35 /* shchecksum.c copied here */
36
37 /*
38  * This module implements the Secure Hash Algorithm (SHA) as specified in
39  * the Secure Hash Standard (SHS, FIPS PUB 180.1).
40  */
41
42 static int big_endian;
43
44 static const sha_int hashinit[] = {
45     0x67452301, 0xEFCDAB89, 0x98BADCFE,
46     0x10325476, 0xC3D2E1F0
47 };
48
49 #define ROTL(n, x) (((x) << (n)) | ((x) >> (SHA_BITS_PER_INT - (n))))
50
51 #ifdef DISABLED_CODE_HERE
52 static sha_int
53 f(int t, sha_int x, sha_int y, sha_int z)
54 {
55     if (t < 0 || t >= SHA_ROUNDS)
56         return 0;
57     if (t < 20)
58         return (z ^ (x & (y ^ z)));
59     if (t < 40)
60         return (x ^ y ^ z);
61     if (t < 60)
62         return ((x & y) | (z & (x | y)));       /* saves 1 boolean op */
63     return (x ^ y ^ z);         /* 60-79 same as 40-59 */
64 }
65 #endif
66
67 /* This is the "magic" function used for each round.         */
68 /* Were this a C function, the interface would be:           */
69 /* static sha_int f(int t, sha_int x, sha_int y, sha_int z) */
70 /* The function call version preserved above until stable    */
71
72 #define f_a(x, y, z) (z ^ (x & (y ^ z)))
73 #define f_b(x, y, z) (x ^ y ^ z)
74 #define f_c(x, y, z) (( (x & y) | (z & (x | y))))
75
76 #define f(t, x, y, z)                     \
77       ( (t < 0 || t >= SHA_ROUNDS) ? 0 :  \
78           ( (t < 20) ? f_a(x, y, z) :     \
79               ( (t < 40) ? f_b(x, y, z) : \
80                   ( (t < 60) ? f_c(x, y, z) : f_b(x, y, z)))))
81
82 /*
83  *static sha_int K(int t)
84  *{
85  *    if (t < 0 || t >= SHA_ROUNDS) return 0;
86  *   if (t < 20)
87  *      return 0x5A827999;
88  *   if (t < 40)
89  *      return 0x6ED9EBA1;
90  *   if (t < 60)
91  *      return 0x8F1BBCDC;
92  *   return 0xCA62C1D6;
93  * }
94  */
95
96 /* This macro/function supplies the "magic" constant for each round. */
97 /* The function call version preserved above until stable            */
98
99 static const sha_int k_vals[] =
100     { 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 };
101
102 #define K(t) ( (t < 0 || t >= SHA_ROUNDS) ? 0 : k_vals[ t/20 ] )
103
104 /*
105  * Update the internal state based on the given chunk.
106  */
107 static void
108 transform(shaState * shaStateP, sha_int * chunk)
109 {
110     sha_int A = shaStateP->digest[0];
111     sha_int B = shaStateP->digest[1];
112     sha_int C = shaStateP->digest[2];
113     sha_int D = shaStateP->digest[3];
114     sha_int E = shaStateP->digest[4];
115     sha_int TEMP = 0;
116
117     int t;
118     sha_int W[SHA_ROUNDS];
119
120     for (t = 0; t < SHA_CHUNK_INTS; t++)
121         W[t] = chunk[t];
122     for (; t < SHA_ROUNDS; t++) {
123         TEMP = W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16];
124         W[t] = ROTL(1, TEMP);
125     }
126
127     for (t = 0; t < SHA_ROUNDS; t++) {
128         TEMP = ROTL(5, A) + f(t, B, C, D) + E + W[t] + K(t);
129         E = D;
130         D = C;
131         C = ROTL(30, B);
132         B = A;
133         A = TEMP;
134     }
135
136     shaStateP->digest[0] += A;
137     shaStateP->digest[1] += B;
138     shaStateP->digest[2] += C;
139     shaStateP->digest[3] += D;
140     shaStateP->digest[4] += E;
141 }
142
143
144 /*
145  * This function takes an array of SHA_CHUNK_BYTES bytes
146  * as input and produces an output array of ints that is
147  * SHA_CHUNK_INTS long.
148  */
149 static void
150 buildInts(const char *data, sha_int * chunk)
151 {
152     /*
153      * Need to copy the data because we can't be certain that
154      * the input buffer will be aligned correctly.
155      */
156     memcpy((void *)chunk, (void *)data, SHA_CHUNK_BYTES);
157
158     if (!big_endian) {
159         /* This loop does nothing but waste time on a big endian machine. */
160         int i;
161
162         for (i = 0; i < SHA_CHUNK_INTS; i++)
163             chunk[i] = ntohl(chunk[i]);
164     }
165 }
166
167 /*
168  * This function updates the internal state of the hash by using
169  * buildInts to break the input up into chunks and repeatedly passing
170  * these chunks to transform().
171  */
172 void
173 sha_update(shaState * shaStateP, const char *buffer, int bufferLen)
174 {
175     int i;
176     sha_int chunk[SHA_CHUNK_INTS];
177     sha_int newLo;
178
179     if (buffer == NULL || bufferLen == 0)
180         return;
181
182     newLo = shaStateP->bitcountLo + (bufferLen << 3);
183     if (newLo < shaStateP->bitcountLo)
184         shaStateP->bitcountHi++;
185     shaStateP->bitcountLo = newLo;
186     shaStateP->bitcountHi += ((bufferLen >> (SHA_BITS_PER_INT - 3)) & 0x07);
187
188     /*
189      * If we won't have enough for a full chunk, just tack this
190      * buffer onto the leftover piece and return.
191      */
192     if (shaStateP->leftoverLen + bufferLen < SHA_CHUNK_BYTES) {
193         memcpy((void *)&(shaStateP->leftover[shaStateP->leftoverLen]),
194                (void *)buffer, bufferLen);
195         shaStateP->leftoverLen += bufferLen;
196         return;
197     }
198
199     /* If we have a leftover chunk, process it first. */
200     if (shaStateP->leftoverLen > 0) {
201         i = (SHA_CHUNK_BYTES - shaStateP->leftoverLen);
202         memcpy((void *)&(shaStateP->leftover[shaStateP->leftoverLen]),
203                (void *)buffer, i);
204         buffer += i;
205         bufferLen -= i;
206         buildInts(shaStateP->leftover, chunk);
207         shaStateP->leftoverLen = 0;
208         transform(shaStateP, chunk);
209     }
210
211     while (bufferLen >= SHA_CHUNK_BYTES) {
212         buildInts(buffer, chunk);
213         transform(shaStateP, chunk);
214         buffer += SHA_CHUNK_BYTES;
215         bufferLen -= SHA_CHUNK_BYTES;
216     }
217 /*    assert((bufferLen >= 0) && (bufferLen < SHA_CHUNK_BYTES)); */
218     if ((bufferLen < 0) || (bufferLen > SHA_CHUNK_BYTES)) {
219         fprintf(stderr, "apache_afs_cache: ASSERTION FAILED...exiting\n");
220         exit(-1);
221     }
222
223     if (bufferLen > 0) {
224         memcpy((void *)&shaStateP->leftover[0], (void *)buffer, bufferLen);
225         shaStateP->leftoverLen = bufferLen;
226     }
227 }
228
229
230 /*
231  * This method updates the internal state of the hash using
232  * any leftover data plus appropriate padding and incorporation
233  * of the hash bitcount to finish the hash.  The hash value
234  * is not valid until finish() has been called.
235  */
236 void
237 sha_finish(shaState * shaStateP)
238 {
239     sha_int chunk[SHA_CHUNK_INTS];
240     int i;
241
242     if (shaStateP->leftoverLen > (SHA_CHUNK_BYTES - 9)) {
243         shaStateP->leftover[shaStateP->leftoverLen++] = 0x80;
244         memset(&(shaStateP->leftover[shaStateP->leftoverLen]), 0,
245                (SHA_CHUNK_BYTES - shaStateP->leftoverLen));
246         buildInts(shaStateP->leftover, chunk);
247         transform(shaStateP, chunk);
248         memset(chunk, 0, SHA_CHUNK_BYTES);
249     } else {
250         shaStateP->leftover[shaStateP->leftoverLen++] = 0x80;
251         memset(&(shaStateP->leftover[shaStateP->leftoverLen]), 0,
252                (SHA_CHUNK_BYTES - shaStateP->leftoverLen));
253         buildInts(shaStateP->leftover, chunk);
254     }
255     shaStateP->leftoverLen = 0;
256
257     chunk[SHA_CHUNK_INTS - 2] = shaStateP->bitcountHi;
258     chunk[SHA_CHUNK_INTS - 1] = shaStateP->bitcountLo;
259     transform(shaStateP, chunk);
260 }
261
262
263 /*
264  * Initialize the hash to its "magic" initial value specified by the
265  * SHS standard, and clear out the bitcount and leftover vars.
266  * This should be used to initialize an shaState.
267  */
268 void
269 sha_clear(shaState * shaStateP)
270 {
271     big_endian = (0x01020304 == htonl(0x01020304));
272
273     memcpy((void *)&shaStateP->digest[0], (void *)&hashinit[0],
274            SHA_HASH_BYTES);
275     shaStateP->bitcountLo = shaStateP->bitcountHi = 0;
276     shaStateP->leftoverLen = 0;
277 }
278
279
280 /*
281  * Hash the buffer and place the result in *shaStateP.
282  */
283 void
284 sha_hash(shaState * shaStateP, const char *buffer, int bufferLen)
285 {
286     sha_clear(shaStateP);
287     sha_update(shaStateP, buffer, bufferLen);
288     sha_finish(shaStateP);
289 }
290
291
292 /*
293  * Returns the current state of the hash as an array of 20 bytes.
294  * This will be an interim result if finish() has not yet been called.
295  */
296 void
297 sha_bytes(const shaState * shaStateP, char *bytes)
298 {
299     sha_int temp[SHA_HASH_INTS];
300     int i;
301
302     for (i = 0; i < SHA_HASH_INTS; i++)
303         temp[i] = htonl(shaStateP->digest[i]);
304     memcpy(bytes, (void *)&temp[0], SHA_HASH_BYTES);
305 }
306
307
308 /*
309  * Hash function for the AFS login cache
310  */
311 int
312 weblog_login_hash(char *name, char *cell)
313 {
314     char *p;
315     afs_uint32 val;
316     for (val = *name, p = name; *p != '\0'; p++) {
317         val = (val << 2) ^ val ^ (afs_uint32) (*p);
318     }
319     for (p = cell; *p != '\0'; p++) {
320         val = (val << 2) ^ val ^ (afs_uint32) (*p);
321     }
322     return val & (WEBLOG_LOGIN_HASH_SIZE - 1);
323 }
324
325 /*
326  * Compute a SHA checksum on the username, cellname, and password
327  */
328 void
329 weblog_login_checksum(char *user, char *cell, char *passwd, char *cksum)
330 {
331     int passwdLen;
332     int userLen;
333     int cellLen;
334     char *shaBuffer;
335     shaState state;
336
337     /*
338      * Compute SHA(username,SHA(password,pad))
339      */
340     passwdLen = strlen(passwd);
341     userLen = strlen(user);
342     cellLen = strlen(cell);
343     shaBuffer =
344         (char *)malloc(MAX(userLen + cellLen, passwdLen) + SHA_HASH_BYTES);
345     strcpy(shaBuffer, passwd);
346     memcpy((void *)(shaBuffer + passwdLen), (void *)(&weblog_login_pad[0]),
347            SHA_HASH_BYTES);
348     sha_clear(&state);
349     sha_hash(&state, shaBuffer, passwdLen + SHA_HASH_BYTES);
350     memcpy(shaBuffer, user, userLen);
351     memcpy(shaBuffer + userLen, cell, cellLen);
352     sha_bytes(&state, shaBuffer + userLen + cellLen);
353     sha_clear(&state);
354     sha_hash(&state, shaBuffer, userLen + cellLen + SHA_HASH_BYTES);
355     sha_bytes(&state, &cksum[0]);
356     memset(shaBuffer, 0, MAX(userLen + cellLen, passwdLen) + SHA_HASH_BYTES);
357     free(shaBuffer);
358 }
359
360 /*
361  * Look up a login ID in the cache. If an entry name is found for the
362  * given username, and the SHA checksums match, then 
363  * set the token parameter and return 1, otherwise return 0.
364  */
365 int
366 weblog_login_lookup(char *user, char *cell, char *cksum, char *token)
367 {
368     int index;
369     long curTime;
370     struct weblog_login *loginP, *tmpP, loginTmp;
371
372     /*
373      * Search the hash chain for a matching entry, free
374      * expired entries as we search
375      */
376     index = weblog_login_hash(user, cell);
377     curTime = time(NULL);
378
379     loginP = weblog_login_cache[index].head;
380     while (loginP != NULL) {
381         if (loginP->expiration < curTime) {
382             tmpP = loginP;
383             loginP = tmpP->next;
384
385             DLL_DELETE(tmpP, weblog_login_cache[index].head,
386                        weblog_login_cache[index].tail, next, prev);
387             free(tmpP);
388             continue;
389         }
390         if (strcmp(loginP->username, user) == 0
391             && strcmp(loginP->cellname, cell) == 0
392             && memcmp((void *)&loginP->cksum[0], (void *)cksum,
393                       SHA_HASH_BYTES) == 0) {
394
395             memcpy((void *)token, (void *)&loginP->token[0], MAXBUFF);
396             return loginP->tokenLen;
397         }
398         loginP = loginP->next;
399     }
400     return 0;
401 }
402
403 /*
404  * Insert a login token into the cache. If the user already has an entry,
405  * then overwrite the old entry.
406  */
407 int
408 weblog_login_store(char *user, char *cell, char *cksum, char *token,
409                    int tokenLen, afs_uint32 expiration)
410 {
411     int index;
412     long curTime;
413     struct weblog_login *loginP, *tmpP, loginTmp;
414
415     int parseToken(char *tokenBuf);
416
417     /*
418      * Search the hash chain for a matching entry, free
419      * expired entries as we search
420      */
421     index = weblog_login_hash(user, cell);
422     curTime = time(NULL);
423     loginP = weblog_login_cache[index].head;
424
425     while (loginP != NULL) {
426         if (strcmp(loginP->username, user) == 0
427             && strcmp(loginP->cellname, cell) == 0) {
428             break;
429         }
430         if (loginP->expiration < curTime) {
431             tmpP = loginP;
432             loginP = tmpP->next;
433
434             DLL_DELETE(tmpP, weblog_login_cache[index].head,
435                        weblog_login_cache[index].tail, next, prev);
436             free(tmpP);
437             continue;
438         }
439         loginP = loginP->next;
440     }
441     if (loginP == NULL) {
442         loginP = (struct weblog_login *)malloc(sizeof(struct weblog_login));
443         strcpy(&loginP->username[0], user);
444         strcpy(&loginP->cellname[0], cell);
445     } else {
446         DLL_DELETE(loginP, weblog_login_cache[index].head,
447                    weblog_login_cache[index].tail, next, prev);
448     }
449
450     memcpy((void *)&loginP->cksum[0], (void *)cksum, SHA_HASH_BYTES);
451     loginP->expiration = expiration;
452     loginP->tokenLen = getTokenLen(token);
453     memcpy((void *)&loginP->token[0], (void *)token, MAXBUFF);
454
455     DLL_INSERT_TAIL(loginP, weblog_login_cache[index].head,
456                     weblog_login_cache[index].tail, next, prev);
457     return 0;
458 }
459
460 token_cache_init()
461 {
462     int i;
463     for (i = 0; i < WEBLOG_LOGIN_HASH_SIZE; i++) {
464         DLL_INIT_LIST(weblog_login_cache[i].head, weblog_login_cache[i].tail);
465     }
466 }
467
468 int
469 getTokenLen(char *buf)
470 {
471     afs_int32 len = 0;
472     afs_int32 rc = 0;
473     char cellName[WEBLOG_CELLNAME_MAX];
474     register char *tp;
475     int n = sizeof(afs_int32);
476     struct ClearToken {
477         afs_int32 AuthHandle;
478         char HandShakeKey[8];
479         afs_int32 ViceId;
480         afs_int32 BeginTimestamp;
481         afs_int32 EndTimestamp;
482     } token;
483     tp = buf;
484     memcpy(&len, tp, sizeof(afs_int32));        /* get size of secret token */
485     rc = (len + sizeof(afs_int32));
486     tp += (sizeof(afs_int32) + len);    /* skip secret token and its length */
487     memcpy(&len, tp, sizeof(afs_int32));        /* get size of clear token */
488     if (len != sizeof(struct ClearToken)) {
489 #ifdef DEBUG
490         fprintf(stderr,
491                 "apache_afs_cache.c:getExpiration:"
492                 "something's wrong with the length of ClearToken:%d\n", len);
493 #endif
494         return -1;
495     }
496     rc += (sizeof(afs_int32) + len);    /* length of clear token + length itself */
497     tp += (sizeof(afs_int32) + len);    /* skip clear token  and its length */
498     rc += sizeof(afs_int32);    /* length of primary flag */
499     tp += sizeof(afs_int32);    /* skip over primary flag */
500     strcpy(cellName, tp);
501     if (cellName != NULL)
502         rc += strlen(cellName);
503     return rc;
504 }
505
506 long
507 getExpiration(char *buf)
508 {
509     afs_int32 len = 0;
510     register char *tp;
511     int n = sizeof(afs_int32);
512     struct ClearToken {
513         afs_int32 AuthHandle;
514         char HandShakeKey[8];
515         afs_int32 ViceId;
516         afs_int32 BeginTimestamp;
517         afs_int32 EndTimestamp;
518     } token;
519
520     tp = buf;
521     memcpy(&len, tp, sizeof(afs_int32));        /* get size of secret token */
522     tp += (sizeof(afs_int32) + len);    /* skip secret token and its length */
523     memcpy(&len, tp, sizeof(afs_int32));        /* get size of clear token */
524     if (len != sizeof(struct ClearToken)) {
525 #ifdef DEBUG
526         fprintf(stderr,
527                 "apache_afs_cache.c:getExpiration:"
528                 "something's wrong with the length of ClearToken:%d\n", len);
529 #endif
530         return -1;
531     }
532
533     tp += sizeof(afs_int32);    /* skip length of clear token */
534     memcpy(&token, tp, sizeof(struct ClearToken));      /* copy the token */
535     return token.EndTimestamp;
536 }