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