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