Commit | Line | Data |
---|---|---|
805e021f CE |
1 | /* |
2 | * Copyright (c) 2010 Your Filesystem Inc. All rights reserved. | |
3 | * | |
4 | * Redistribution and use in source and binary forms, with or without | |
5 | * modification, are permitted provided that the following conditions | |
6 | * are met: | |
7 | * 1. Redistributions of source code must retain the above copyright | |
8 | * notice, this list of conditions and the following disclaimer. | |
9 | * 2. Redistributions in binary form must reproduce the above copyright | |
10 | * notice, this list of conditions and the following disclaimer in the | |
11 | * documentation and/or other materials provided with the distribution. | |
12 | * | |
13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR | |
14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
23 | */ | |
24 | ||
25 | #include <afsconfig.h> | |
26 | #include <afs/param.h> | |
27 | ||
28 | #include <roken.h> | |
29 | ||
30 | #include "auth.h" | |
31 | #include <rx/rxkad.h> | |
32 | ||
33 | #include "ktc.h" | |
34 | #include "token.h" | |
35 | ||
36 | ||
37 | /* Routines for processing tokens in the new XDR format | |
38 | * | |
39 | * The code here is inspired by work done by Jeffrey Hutzelman et al | |
40 | * at the AFSSIG.se hackathon and further refined by Matt | |
41 | * Benjamin and Marcus Watts as part of rxk5. However, unless | |
42 | * otherwise noted, the implementation is new | |
43 | */ | |
44 | ||
45 | /* Take a peak at the enumerator in a given encoded token, in order to | |
46 | * return its type | |
47 | */ | |
48 | static int | |
49 | tokenType(struct token_opaque *opaque) { | |
50 | XDR xdrs; | |
51 | int type; | |
52 | ||
53 | xdrmem_create(&xdrs, opaque->token_opaque_val, opaque->token_opaque_len, | |
54 | XDR_DECODE); | |
55 | ||
56 | if (!xdr_enum(&xdrs, &type)) | |
57 | type = -1; | |
58 | ||
59 | xdr_destroy(&xdrs); | |
60 | ||
61 | return type; | |
62 | } | |
63 | ||
64 | static int | |
65 | decodeToken(struct token_opaque *opaque, struct ktc_tokenUnion *token) { | |
66 | XDR xdrs; | |
67 | int code; | |
68 | ||
69 | memset(token, 0, sizeof(struct ktc_tokenUnion)); | |
70 | xdrmem_create(&xdrs, opaque->token_opaque_val, opaque->token_opaque_len, | |
71 | XDR_DECODE); | |
72 | code = xdr_ktc_tokenUnion(&xdrs, token); | |
73 | xdr_destroy(&xdrs); | |
74 | ||
75 | return code; | |
76 | } | |
77 | ||
78 | static int | |
79 | rxkadTokenEqual(struct ktc_tokenUnion *tokenA, struct ktc_tokenUnion *tokenB) { | |
80 | return (tokenA->ktc_tokenUnion_u.at_kad.rk_kvno == | |
81 | tokenB->ktc_tokenUnion_u.at_kad.rk_kvno | |
82 | && tokenA->ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_len == | |
83 | tokenB->ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_len | |
84 | && !memcmp(tokenA->ktc_tokenUnion_u.at_kad.rk_key, | |
85 | tokenB->ktc_tokenUnion_u.at_kad.rk_key, 8) | |
86 | && !memcmp(tokenA->ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_val, | |
87 | tokenB->ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_val, | |
88 | tokenA->ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_len)); | |
89 | } | |
90 | ||
91 | static int | |
92 | tokenEqual(struct ktc_tokenUnion *tokenA, | |
93 | struct ktc_tokenUnion *tokenB) { | |
94 | switch (tokenA->at_type) { | |
95 | case AFSTOKEN_UNION_KAD: | |
96 | return rxkadTokenEqual(tokenA, tokenB); | |
97 | } | |
98 | return 0; | |
99 | } | |
100 | ||
101 | static int | |
102 | rawTokenEqual(struct token_opaque *tokenA, struct token_opaque *tokenB) { | |
103 | return (tokenA->token_opaque_len == tokenB->token_opaque_len && | |
104 | !memcmp(tokenA->token_opaque_val, tokenB->token_opaque_val, | |
105 | tokenA->token_opaque_len)); | |
106 | } | |
107 | ||
108 | /* Given a token type, return the entry number of the first token of that | |
109 | * type */ | |
110 | static int | |
111 | findTokenEntry(struct ktc_setTokenData *token, | |
112 | int targetType) | |
113 | { | |
114 | int i; | |
115 | ||
116 | for (i = 0; i < token->tokens.tokens_len; i++) { | |
117 | if (tokenType(&token->tokens.tokens_val[i]) == targetType) | |
118 | return i; | |
119 | } | |
120 | return -1; | |
121 | } | |
122 | ||
123 | /* XDR encode a token union structure, and return data and length information | |
124 | * suitable for stuffing into a token_opaque structure | |
125 | */ | |
126 | static int | |
127 | encodeTokenUnion(struct ktc_tokenUnion *token, | |
128 | char **dataPtr, size_t *lenPtr) { | |
129 | char *data = NULL; | |
130 | size_t len; | |
131 | XDR xdrs; | |
132 | int code = 0; | |
133 | ||
134 | *dataPtr = NULL; | |
135 | *lenPtr = 0; | |
136 | ||
137 | xdrlen_create(&xdrs); | |
138 | if (!xdr_ktc_tokenUnion(&xdrs, token)) { | |
139 | code = EINVAL; | |
140 | goto out; | |
141 | } | |
142 | ||
143 | len = xdr_getpos(&xdrs); | |
144 | data = malloc(len); | |
145 | if (data == NULL) { | |
146 | code = ENOMEM; | |
147 | goto out; | |
148 | } | |
149 | xdr_destroy(&xdrs); | |
150 | ||
151 | xdrmem_create(&xdrs, data, len, XDR_ENCODE); | |
152 | if (!xdr_ktc_tokenUnion(&xdrs, token)) { | |
153 | code = EINVAL; | |
154 | goto out; | |
155 | } | |
156 | ||
157 | *dataPtr = data; | |
158 | *lenPtr = len; | |
159 | ||
160 | out: | |
161 | xdr_destroy(&xdrs); | |
162 | if (code) { | |
163 | if (data) | |
164 | free(data); | |
165 | } | |
166 | ||
167 | return code; | |
168 | } | |
169 | ||
170 | static void | |
171 | addOpaque(struct ktc_setTokenData *jar, char *data, size_t len) | |
172 | { | |
173 | int entry; | |
174 | ||
175 | entry = jar->tokens.tokens_len; | |
176 | jar->tokens.tokens_val = realloc(jar->tokens.tokens_val, | |
177 | (entry + 1) * sizeof(token_opaque)); | |
178 | jar->tokens.tokens_len++; | |
179 | jar->tokens.tokens_val[entry].token_opaque_val = data; | |
180 | jar->tokens.tokens_val[entry].token_opaque_len = len; | |
181 | } | |
182 | ||
183 | /*! | |
184 | * Extract a specific token element from a unified token structure | |
185 | * | |
186 | * This routine extracts an afsTokenUnion structure from the tokenData | |
187 | * structure used by the SetTokenEx and GetTokenEx pioctls | |
188 | * | |
189 | * @param[in] token | |
190 | * A ktc_setTokenData structure containing the token to extract from | |
191 | * @param[in] targetType | |
192 | * The securityClass index of the token to be extracted | |
193 | * @param[out] output | |
194 | * The decoded token. On entry, this must point to a block of memory | |
195 | * of sufficient size to contain an afsTokenUnion structure. Upon | |
196 | * completion, this block must be passed to xdr_free(), using the | |
197 | * xdr_afsTokenUnion xdrproc_t. | |
198 | */ | |
199 | int | |
200 | token_findByType(struct ktc_setTokenData *token, | |
201 | int targetType, | |
202 | struct ktc_tokenUnion *output) | |
203 | { | |
204 | int entry; | |
205 | ||
206 | memset(output, 0, sizeof *output); | |
207 | entry = findTokenEntry(token, targetType); | |
208 | if (entry == -1) | |
209 | return EINVAL; | |
210 | ||
211 | if (!decodeToken(&token->tokens.tokens_val[entry], output)) | |
212 | return EINVAL; | |
213 | ||
214 | if (output->at_type != targetType) { | |
215 | xdr_free((xdrproc_t)xdr_ktc_tokenUnion, output); | |
216 | return EINVAL; | |
217 | } | |
218 | ||
219 | return 0; | |
220 | } | |
221 | ||
222 | static void | |
223 | SetRxkadViceId(struct token_rxkad *rxkadToken, afs_int32 viceId) | |
224 | { | |
225 | rxkadToken->rk_viceid = viceId; | |
226 | if (viceId) { | |
227 | if (((rxkadToken->rk_endtime - rxkadToken->rk_begintime) & 1) == 0) { | |
228 | rxkadToken->rk_begintime++; /* force lifetime to be odd */ | |
229 | } | |
230 | } else { | |
231 | if (((rxkadToken->rk_endtime - rxkadToken->rk_begintime) & 1) == 1) { | |
232 | rxkadToken->rk_begintime++; /* force lifetime to be even */ | |
233 | } | |
234 | } | |
235 | } | |
236 | ||
237 | /** | |
238 | * Import an rxkad token with a ViceId into a unified token. | |
239 | * | |
240 | * @param[out] atoken | |
241 | * The resultant unified token. Free with token_freeToken. | |
242 | * @param[in] oldToken | |
243 | * The rxkad token to import. | |
244 | * @param[in] viceId | |
245 | * The optional rxkad ViceId to use. Specify 0 to explicitly not | |
246 | * specify a ViceId. | |
247 | * | |
248 | * @return operation status | |
249 | * @retval 0 success | |
250 | */ | |
251 | int | |
252 | token_importRxkadViceId(struct ktc_tokenUnion **atoken, | |
253 | struct ktc_token *oldToken, | |
254 | afs_int32 viceId) | |
255 | { | |
256 | struct ktc_tokenUnion *token; | |
257 | struct token_rxkad *rxkadToken; | |
258 | ||
259 | token = malloc(sizeof(struct ktc_tokenUnion)); | |
260 | if (!token) | |
261 | return ENOMEM; | |
262 | ||
263 | token->at_type = AFSTOKEN_UNION_KAD; | |
264 | rxkadToken = &token->ktc_tokenUnion_u.at_kad; | |
265 | ||
266 | rxkadToken->rk_kvno = oldToken->kvno; | |
267 | rxkadToken->rk_begintime = oldToken->startTime; | |
268 | rxkadToken->rk_endtime = oldToken->endTime; | |
269 | memcpy(&rxkadToken->rk_key, &oldToken->sessionKey, | |
270 | sizeof(oldToken->sessionKey)); | |
271 | rxkadToken->rk_ticket.rk_ticket_len = oldToken->ticketLen; | |
272 | ||
273 | rxkadToken->rk_ticket.rk_ticket_val = xdr_alloc(oldToken->ticketLen); | |
274 | if (!rxkadToken->rk_ticket.rk_ticket_val) { | |
275 | free(token); | |
276 | return ENOMEM; | |
277 | } | |
278 | memcpy(rxkadToken->rk_ticket.rk_ticket_val, oldToken->ticket, oldToken->ticketLen); | |
279 | ||
280 | SetRxkadViceId(rxkadToken, viceId); | |
281 | ||
282 | *atoken = token; | |
283 | ||
284 | return 0; | |
285 | } | |
286 | ||
287 | /** | |
288 | * Set the optional ViceId for an rxkad token. | |
289 | * | |
290 | * @param[in] token | |
291 | * The token union to change. | |
292 | * @param[in] viceId | |
293 | * The ViceId to set. Specify 0 to explicitly set no ViceId. | |
294 | * | |
295 | * @return operation status | |
296 | * @retval EINVAL The given token union is not an rxkad token | |
297 | * @retval 0 success | |
298 | */ | |
299 | int | |
300 | token_setRxkadViceId(struct ktc_tokenUnion *token, | |
301 | afs_int32 viceId) | |
302 | { | |
303 | struct token_rxkad *rxkadToken; | |
304 | ||
305 | if (token->at_type != AFSTOKEN_UNION_KAD) { | |
306 | return EINVAL; | |
307 | } | |
308 | ||
309 | rxkadToken = &token->ktc_tokenUnion_u.at_kad; | |
310 | SetRxkadViceId(rxkadToken, viceId); | |
311 | ||
312 | return 0; | |
313 | } | |
314 | ||
315 | /*! | |
316 | * Given an unified token, populate an rxkad token from it | |
317 | * | |
318 | * This routine populates an rxkad token using information contained | |
319 | * in the tokenData structure used by the SetTokenEx and GetTokenEX | |
320 | * pioctls. | |
321 | * | |
322 | * @param[in] token | |
323 | * The new format token to extract information from. | |
324 | * @param[out] rxkadToken | |
325 | * The old-style rxkad token. This must be a pointer to an existing | |
326 | * data block of sufficient size | |
327 | * @param[out] flags | |
328 | * The set of token flags | |
329 | * @param[out] aclient | |
330 | * The client owning the token. This must be a pointer to an existing | |
331 | * data block of sufficient size, or NULL. | |
332 | */ | |
333 | ||
334 | int | |
335 | token_extractRxkad(struct ktc_setTokenData *token, | |
336 | struct ktc_token *rxkadToken, | |
337 | int *flags, | |
338 | struct ktc_principal *aclient) | |
339 | { | |
340 | struct ktc_tokenUnion uToken; | |
341 | int code; | |
342 | ||
343 | memset(&uToken, 0, sizeof(uToken)); | |
344 | if (aclient) | |
345 | memset(aclient, 0, sizeof(*aclient)); | |
346 | ||
347 | code = token_findByType(token, AFSTOKEN_UNION_KAD, &uToken); | |
348 | if (code) | |
349 | return code; | |
350 | ||
351 | rxkadToken->kvno = uToken.ktc_tokenUnion_u.at_kad.rk_kvno; | |
352 | memcpy(rxkadToken->sessionKey.data, | |
353 | uToken.ktc_tokenUnion_u.at_kad.rk_key, 8); | |
354 | rxkadToken->startTime = uToken.ktc_tokenUnion_u.at_kad.rk_begintime; | |
355 | rxkadToken->endTime = uToken.ktc_tokenUnion_u.at_kad.rk_endtime; | |
356 | rxkadToken->ticketLen = uToken.ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_len; | |
357 | ||
358 | if (rxkadToken->ticketLen > MAXKTCTICKETLEN) { | |
359 | code = E2BIG; | |
360 | goto out; | |
361 | } | |
362 | ||
363 | memcpy(rxkadToken->ticket, | |
364 | uToken.ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_val, | |
365 | rxkadToken->ticketLen); | |
366 | ||
367 | if (flags) { | |
368 | *flags = 0; | |
369 | if ((token->flags & AFSTOKEN_EX_SETPAG)) { | |
370 | *flags |= AFS_SETTOK_SETPAG; | |
371 | } | |
372 | } | |
373 | ||
374 | if (aclient) { | |
375 | strncpy(aclient->cell, token->cell, MAXKTCREALMLEN-1); | |
376 | aclient->cell[MAXKTCREALMLEN-1] = '\0'; | |
377 | ||
378 | if ((rxkadToken->kvno == 999) || /* old style bcrypt ticket */ | |
379 | (rxkadToken->startTime && /* new w/ prserver lookup */ | |
380 | (((rxkadToken->endTime - rxkadToken->startTime) & 1) == 1))) { | |
381 | sprintf(aclient->name, "AFS ID %d", | |
382 | uToken.ktc_tokenUnion_u.at_kad.rk_viceid); | |
383 | } else { | |
384 | sprintf(aclient->name, "Unix UID %d", | |
385 | uToken.ktc_tokenUnion_u.at_kad.rk_viceid); | |
386 | } | |
387 | } | |
388 | ||
389 | out: | |
390 | xdr_free((xdrproc_t) xdr_ktc_tokenUnion, &uToken); | |
391 | return code; | |
392 | } | |
393 | ||
394 | struct ktc_setTokenData * | |
395 | token_buildTokenJar(char * cellname) { | |
396 | struct ktc_setTokenData *jar; | |
397 | ||
398 | jar = calloc(1, sizeof(struct ktc_setTokenData)); | |
399 | if (jar == NULL) | |
400 | return NULL; | |
401 | ||
402 | jar->cell = strdup(cellname); | |
403 | ||
404 | return jar; | |
405 | } | |
406 | ||
407 | /*! | |
408 | * Add a token to an existing set of tokens. This will always add the token, | |
409 | * regardless of whether an entry for the security class already exists | |
410 | */ | |
411 | int | |
412 | token_addToken(struct ktc_setTokenData *jar, struct ktc_tokenUnion *token) { | |
413 | int code; | |
414 | char *data; | |
415 | size_t len; | |
416 | ||
417 | code = encodeTokenUnion(token, &data, &len); | |
418 | if (code) | |
419 | goto out; | |
420 | ||
421 | addOpaque(jar, data, len); | |
422 | ||
423 | out: | |
424 | return code; | |
425 | } | |
426 | ||
427 | /*! | |
428 | * Replace at token in an existing set of tokens. This replaces the first | |
429 | * token stored of a matching type. If no matching tokens are found, then | |
430 | * the new token is added at the end of the list | |
431 | */ | |
432 | int | |
433 | token_replaceToken(struct ktc_setTokenData *jar, | |
434 | struct ktc_tokenUnion *token) { | |
435 | int entry; | |
436 | char *data; | |
437 | size_t len; | |
438 | int code; | |
439 | ||
440 | entry = findTokenEntry(jar, token->at_type); | |
441 | if (entry == -1) | |
442 | return token_addToken(jar, token); | |
443 | ||
444 | code = encodeTokenUnion(token, &data, &len); | |
445 | if (code) | |
446 | goto out; | |
447 | ||
448 | free(jar->tokens.tokens_val[entry].token_opaque_val); | |
449 | jar->tokens.tokens_val[entry].token_opaque_val = data; | |
450 | jar->tokens.tokens_val[entry].token_opaque_len = len; | |
451 | ||
452 | out: | |
453 | return code; | |
454 | } | |
455 | ||
456 | /*! | |
457 | * Work out if a pair of token sets are equivalent. Equivalence | |
458 | * is defined as both sets containing the same number of tokens, | |
459 | * and every token in the first set having an equivalent token | |
460 | * in the second set. Cell name and flags value are not compared. | |
461 | * | |
462 | * @param[in] tokensA | |
463 | * First set of tokens | |
464 | * @param[in] tokensB | |
465 | * Second set of tokens | |
466 | * | |
467 | * @returns | |
468 | * True if token sets are equivalent, false otherwise | |
469 | */ | |
470 | int | |
471 | token_SetsEquivalent(struct ktc_setTokenData *tokenSetA, | |
472 | struct ktc_setTokenData *tokenSetB) { | |
473 | int i, j; | |
474 | int decodedOK, found; | |
475 | struct ktc_tokenUnion tokenA, tokenB; | |
476 | ||
477 | if (tokenSetA->tokens.tokens_len != tokenSetB->tokens.tokens_len) | |
478 | return 0; | |
479 | ||
480 | for (i=0; i<tokenSetA->tokens.tokens_len; i++) { | |
481 | found = 0; | |
482 | ||
483 | decodedOK = decodeToken(&tokenSetA->tokens.tokens_val[i], &tokenA); | |
484 | ||
485 | for (j=0; j<tokenSetB->tokens.tokens_len && !found; j++) { | |
486 | if (rawTokenEqual(&tokenSetA->tokens.tokens_val[i], | |
487 | &tokenSetB->tokens.tokens_val[j])) { | |
488 | found = 1; | |
489 | break; | |
490 | } | |
491 | ||
492 | if (decodedOK && | |
493 | tokenType(&tokenSetB->tokens.tokens_val[j]) == tokenA.at_type | |
494 | && decodeToken(&tokenSetB->tokens.tokens_val[j], &tokenB)) { | |
495 | ||
496 | if (tokenEqual(&tokenA, &tokenB)) { | |
497 | found = 1; | |
498 | break; | |
499 | } | |
500 | token_freeTokenContents(&tokenB); | |
501 | } | |
502 | } | |
503 | if (decodedOK) | |
504 | token_freeTokenContents(&tokenA); | |
505 | ||
506 | if (!found) | |
507 | return 0; | |
508 | } | |
509 | /* If we made it this far without exiting, we must have found equivalents | |
510 | * for all of our tokens */ | |
511 | return 1; | |
512 | } | |
513 | ||
514 | void | |
515 | token_setPag(struct ktc_setTokenData *jar, int setpag) { | |
516 | if (setpag) | |
517 | jar->flags |= AFSTOKEN_EX_SETPAG; | |
518 | else | |
519 | jar->flags &= ~AFSTOKEN_EX_SETPAG; | |
520 | } | |
521 | ||
522 | void | |
523 | token_freeTokenContents(struct ktc_tokenUnion *atoken) | |
524 | { | |
525 | xdr_free((xdrproc_t)xdr_ktc_tokenUnion, atoken); | |
526 | } | |
527 | ||
528 | void | |
529 | token_freeToken(struct ktc_tokenUnion **atoken) | |
530 | { | |
531 | if (*atoken) { | |
532 | token_freeTokenContents(*atoken); | |
533 | free(*atoken); | |
534 | *atoken = NULL; | |
535 | } | |
536 | } | |
537 | ||
538 | void | |
539 | token_FreeSet(struct ktc_setTokenData **jar) { | |
540 | if (*jar) { | |
541 | xdr_free((xdrproc_t)xdr_ktc_setTokenData, *jar); | |
542 | memset(*jar, 0, sizeof(struct ktc_setTokenData)); | |
543 | *jar = NULL; | |
544 | } | |
545 | } |