Commit | Line | Data |
---|---|---|
805e021f CE |
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 | } |