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