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 | #include "afs/sysincludes.h" | |
28 | #include "afsincludes.h" | |
29 | #include "token.h" | |
30 | ||
31 | /* A jar for storing tokens in */ | |
32 | ||
33 | /*! | |
34 | * Return a token of the specified type from the selected tokenjar. | |
35 | * | |
36 | * @param[in] tokens | |
37 | * The tokenjar in which to search | |
38 | * @param[in] type | |
39 | * The type of token to return | |
40 | * | |
41 | * @return | |
42 | * A tokenUnion structure, from which the desired token can be | |
43 | * accessed using the appropriate element of the union. | |
44 | */ | |
45 | union tokenUnion * | |
46 | afs_FindToken(struct tokenJar *tokens, rx_securityIndex type) { | |
47 | while (tokens != NULL) { | |
48 | if (tokens->type == type) { | |
49 | return &tokens->content; | |
50 | } | |
51 | tokens = tokens->next; | |
52 | } | |
53 | return NULL; | |
54 | } | |
55 | ||
56 | /*! | |
57 | * Free a single token | |
58 | * | |
59 | * This will free the given token. No attempt is made to unlink | |
60 | * the token from its container, and it is an error to attempt to | |
61 | * free a token which is still linked. | |
62 | * | |
63 | * This performs a secure free, setting all token information to 0 | |
64 | * before returning allocated data blocks to the kernel. | |
65 | * | |
66 | * Intended primarily for internal use. | |
67 | * | |
68 | * @param[in] token | |
69 | * The token to free | |
70 | */ | |
71 | ||
72 | void | |
73 | afs_FreeOneToken(struct tokenJar *token) { | |
74 | if (token->next != NULL) | |
75 | osi_Panic("Freeing linked token"); | |
76 | ||
77 | switch (token->type) { | |
78 | case RX_SECIDX_KAD: | |
79 | if (token->content.rxkad.ticket != NULL) { | |
80 | memset(token->content.rxkad.ticket, 0, token->content.rxkad.ticketLen); | |
81 | afs_osi_Free(token->content.rxkad.ticket, | |
82 | token->content.rxkad.ticketLen); | |
83 | } | |
84 | break; | |
85 | default: | |
86 | break; | |
87 | } | |
88 | memset(token, 0, sizeof(struct tokenJar)); | |
89 | afs_osi_Free(token, sizeof(struct tokenJar)); | |
90 | } | |
91 | ||
92 | /*! | |
93 | * Free a token jar | |
94 | * | |
95 | * Free all of the tokens in a given token jar. This will also set the | |
96 | * pointer to the jar to NULL, to indicate that it has been freed. | |
97 | * | |
98 | * @param[in] tokenPtr | |
99 | * A pointer to the address of the tokenjar to free. | |
100 | */ | |
101 | void | |
102 | afs_FreeTokens(struct tokenJar **tokenPtr) { | |
103 | struct tokenJar *next, *tokens; | |
104 | ||
105 | tokens = *tokenPtr; | |
106 | *tokenPtr = NULL; | |
107 | while(tokens != NULL) { | |
108 | next = tokens->next; | |
109 | tokens->next = NULL; /* Unlink from chain */ | |
110 | afs_FreeOneToken(tokens); | |
111 | tokens = next; | |
112 | } | |
113 | } | |
114 | ||
115 | /*! | |
116 | * Add a token to a token jar | |
117 | * | |
118 | * Add a new token to a token jar. If the jar already exists, | |
119 | * then this token becomes the first in the jar. If it doesn't | |
120 | * exist, then a new jar is created. The contents of the new | |
121 | * token are initialised to 0 upon creation. | |
122 | * | |
123 | * @param[in] tokens | |
124 | * A pointer to the address of the token jar to populate | |
125 | * @param[in] type | |
126 | * The type of token to create | |
127 | * | |
128 | * @return | |
129 | * A pointer to the tokenUnion of the newly created token, | |
130 | * which may then be used to populate the token. | |
131 | */ | |
132 | union tokenUnion * | |
133 | afs_AddToken(struct tokenJar **tokens, rx_securityIndex type) { | |
134 | struct tokenJar *newToken; | |
135 | ||
136 | newToken = afs_osi_Alloc(sizeof(struct tokenJar)); | |
137 | osi_Assert(newToken != NULL); | |
138 | memset(newToken, 0, sizeof(*newToken)); | |
139 | ||
140 | newToken->type = type; | |
141 | newToken->next = *tokens; | |
142 | *tokens = newToken; | |
143 | ||
144 | return &newToken->content; | |
145 | } | |
146 | ||
147 | /*! | |
148 | * Indicate if a single token is expired | |
149 | * | |
150 | * @param[in] token | |
151 | * The token to check | |
152 | * @param[in] now | |
153 | * The time to check against for expiry (typically the results of | |
154 | * calling osi_Time()) | |
155 | * | |
156 | * @returns | |
157 | * True if the token has expired, false otherwise | |
158 | */ | |
159 | int | |
160 | afs_IsTokenExpired(struct tokenJar *token, afs_int32 now) { | |
161 | switch (token->type) { | |
162 | case RX_SECIDX_KAD: | |
163 | if (token->content.rxkad.clearToken.EndTimestamp < now - NOTOKTIMEOUT) | |
164 | return 1; | |
165 | break; | |
166 | default: | |
167 | return 0; | |
168 | } | |
169 | return 0; | |
170 | } | |
171 | ||
172 | /*! | |
173 | * Indicate if a token is usable by the kernel module | |
174 | * | |
175 | * This determines whether a token is usable. A usable token is one that | |
176 | * has not expired, and which is otherwise suitable for use. | |
177 | * | |
178 | * @param[in] token | |
179 | * The token to check | |
180 | * @param[in] now | |
181 | * The time to use for the expiry check | |
182 | * | |
183 | * @returns | |
184 | * True if the token is usable, false otherwise | |
185 | */ | |
186 | int | |
187 | afs_IsTokenUsable(struct tokenJar *token, afs_int32 now) { | |
188 | ||
189 | if (afs_IsTokenExpired(token, now)) | |
190 | return 0; | |
191 | ||
192 | switch (token->type) { | |
193 | case RX_SECIDX_KAD: | |
194 | /* We assume that all non-expired rxkad tokens are usable by us */ | |
195 | return 1; | |
196 | default : | |
197 | return 0; | |
198 | } | |
199 | } | |
200 | ||
201 | /*! | |
202 | * Discard all expired tokens from a token jar | |
203 | * | |
204 | * This permanently removes all tokens which have expired from the token | |
205 | * jar. Note that tokens which are not usable, but which have not expired, | |
206 | * will not be deleted. | |
207 | * | |
208 | * @param[in] tokenPtr | |
209 | * A pointer to the address of the token jar to check | |
210 | * @param[in] now | |
211 | * The time to use for the expiry check | |
212 | */ | |
213 | ||
214 | void | |
215 | afs_DiscardExpiredTokens(struct tokenJar **tokenPtr, afs_int32 now) { | |
216 | struct tokenJar *next; | |
217 | ||
218 | while (*tokenPtr != NULL) { | |
219 | if (afs_IsTokenExpired(*tokenPtr, now)) { | |
220 | next = (*tokenPtr)->next; | |
221 | (*tokenPtr)->next = NULL; | |
222 | afs_FreeOneToken(*tokenPtr); | |
223 | *tokenPtr = next; | |
224 | } else { | |
225 | tokenPtr = &(*tokenPtr)->next; | |
226 | } | |
227 | } | |
228 | } | |
229 | ||
230 | /*! | |
231 | * Indicate whether a token jar contains one, or more usable tokens | |
232 | * | |
233 | * @param[in] token | |
234 | * The token jar to check | |
235 | * @param[in] now | |
236 | * The cime to use for the expiry check | |
237 | * | |
238 | * @returns | |
239 | * True if the jar contains usable tokens, otherwise false | |
240 | */ | |
241 | int | |
242 | afs_HasUsableTokens(struct tokenJar *token, afs_int32 now) { | |
243 | while (token != NULL) { | |
244 | if (afs_IsTokenUsable(token, now)) | |
245 | return 1; | |
246 | token = token->next; | |
247 | } | |
248 | return 0; | |
249 | } | |
250 | ||
251 | /*! | |
252 | * Indicate whether a token jar contains a valid (non-expired) token | |
253 | * | |
254 | * @param[in] token | |
255 | * The token jar to check | |
256 | * @param[in] now | |
257 | * The time to use for the expiry check | |
258 | * | |
259 | * @returns | |
260 | * True if the jar contains valid tokens, otherwise false | |
261 | * | |
262 | */ | |
263 | int | |
264 | afs_HasValidTokens(struct tokenJar *token, afs_int32 now) { | |
265 | while (token != NULL) { | |
266 | if (!afs_IsTokenExpired(token, now)) | |
267 | return 1; | |
268 | token = token->next; | |
269 | } | |
270 | return 0; | |
271 | } | |
272 | ||
273 | /*! | |
274 | * Count the number of valid tokens in a jar. A valid token is | |
275 | * one which is not expired - note that valid tokens may not be | |
276 | * usable by the kernel. | |
277 | * | |
278 | * @param[in] token | |
279 | * The token jar to check | |
280 | * @param[in] now | |
281 | * The time to use for the expiry check | |
282 | * | |
283 | * @returns | |
284 | * The number of valid tokens in the jar | |
285 | */ | |
286 | static int | |
287 | countValidTokens(struct tokenJar *token, time_t now) { | |
288 | int count = 0; | |
289 | ||
290 | while (token != NULL) { | |
291 | if (!afs_IsTokenExpired(token, now)) | |
292 | count ++; | |
293 | token = token->next; | |
294 | } | |
295 | return count; | |
296 | } | |
297 | ||
298 | /*! | |
299 | * Add an rxkad token to the token jar | |
300 | * | |
301 | * @param[in] tokens | |
302 | * A pointer to the address of the jar to add the token to | |
303 | * @param[in] ticket | |
304 | * A data block containing the token's opaque ticket | |
305 | * @param[in] ticketLen | |
306 | * The length of the ticket data block | |
307 | * @param[in] clearToken | |
308 | * The cleartext token information | |
309 | */ | |
310 | void | |
311 | afs_AddRxkadToken(struct tokenJar **tokens, char *ticket, int ticketLen, | |
312 | struct ClearToken *clearToken) { | |
313 | union tokenUnion *tokenU; | |
314 | struct rxkadToken *rxkad; | |
315 | ||
316 | tokenU = afs_AddToken(tokens, RX_SECIDX_KAD); | |
317 | rxkad = &tokenU->rxkad; | |
318 | ||
319 | rxkad->ticket = afs_osi_Alloc(ticketLen); | |
320 | osi_Assert(rxkad->ticket != NULL); | |
321 | rxkad->ticketLen = ticketLen; | |
322 | memcpy(rxkad->ticket, ticket, ticketLen); | |
323 | rxkad->clearToken = *clearToken; | |
324 | } | |
325 | ||
326 | static int | |
327 | afs_AddRxkadTokenFromPioctl(struct tokenJar **tokens, | |
328 | struct ktc_tokenUnion *pioctlToken) { | |
329 | struct ClearToken clear; | |
330 | ||
331 | clear.AuthHandle = pioctlToken->ktc_tokenUnion_u.at_kad.rk_kvno; | |
332 | clear.ViceId = pioctlToken->ktc_tokenUnion_u.at_kad.rk_viceid; | |
333 | clear.BeginTimestamp = pioctlToken->ktc_tokenUnion_u.at_kad.rk_begintime; | |
334 | clear.EndTimestamp = pioctlToken->ktc_tokenUnion_u.at_kad.rk_endtime; | |
335 | memcpy(clear.HandShakeKey, pioctlToken->ktc_tokenUnion_u.at_kad.rk_key, 8); | |
336 | afs_AddRxkadToken(tokens, | |
337 | pioctlToken->ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_val, | |
338 | pioctlToken->ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_len, | |
339 | &clear); | |
340 | ||
341 | /* Security means never having to say you're sorry */ | |
342 | memset(clear.HandShakeKey, 0, 8); | |
343 | ||
344 | return 0; | |
345 | } | |
346 | ||
347 | static int | |
348 | rxkad_extractTokenForPioctl(struct tokenJar *token, | |
349 | struct ktc_tokenUnion *pioctlToken) { | |
350 | ||
351 | struct token_rxkad *rxkadPioctl; | |
352 | struct rxkadToken *rxkadInternal; | |
353 | ||
354 | rxkadPioctl = &pioctlToken->ktc_tokenUnion_u.at_kad; | |
355 | rxkadInternal = &token->content.rxkad; | |
356 | ||
357 | rxkadPioctl->rk_kvno = rxkadInternal->clearToken.AuthHandle; | |
358 | rxkadPioctl->rk_viceid = rxkadInternal->clearToken.ViceId; | |
359 | rxkadPioctl->rk_begintime = rxkadInternal->clearToken.BeginTimestamp; | |
360 | rxkadPioctl->rk_endtime = rxkadInternal->clearToken.EndTimestamp; | |
361 | memcpy(rxkadPioctl->rk_key, rxkadInternal->clearToken.HandShakeKey, 8); | |
362 | ||
363 | rxkadPioctl->rk_ticket.rk_ticket_val = xdr_alloc(rxkadInternal->ticketLen); | |
364 | if (rxkadPioctl->rk_ticket.rk_ticket_val == NULL) | |
365 | return ENOMEM; | |
366 | rxkadPioctl->rk_ticket.rk_ticket_len = rxkadInternal->ticketLen; | |
367 | memcpy(rxkadPioctl->rk_ticket.rk_ticket_val, | |
368 | rxkadInternal->ticket, rxkadInternal->ticketLen); | |
369 | ||
370 | return 0; | |
371 | } | |
372 | ||
373 | /*! | |
374 | * Add a token to a token jar based on the input from a new-style | |
375 | * SetToken pioctl | |
376 | * | |
377 | * @param[in] tokens | |
378 | * Pointer to the address of a token jar | |
379 | * @param[in] pioctlToken | |
380 | * The token structure obtained through the pioctl (note this | |
381 | * is a single, XDR decoded, token) | |
382 | * | |
383 | * @returns | |
384 | * 0 on success, an error code on failure | |
385 | */ | |
386 | int | |
387 | afs_AddTokenFromPioctl(struct tokenJar **tokens, | |
388 | struct ktc_tokenUnion *pioctlToken) { | |
389 | ||
390 | switch (pioctlToken->at_type) { | |
391 | case RX_SECIDX_KAD: | |
392 | return afs_AddRxkadTokenFromPioctl(tokens, pioctlToken); | |
393 | } | |
394 | ||
395 | return EINVAL; | |
396 | } | |
397 | ||
398 | static int | |
399 | extractPioctlToken(struct tokenJar *token, | |
400 | struct token_opaque *opaque) { | |
401 | XDR xdrs; | |
402 | struct ktc_tokenUnion *pioctlToken; | |
403 | int code; | |
404 | ||
405 | memset(opaque, 0, sizeof(token_opaque)); | |
406 | ||
407 | pioctlToken = osi_Alloc(sizeof(struct ktc_tokenUnion)); | |
408 | if (pioctlToken == NULL) | |
409 | return ENOMEM; | |
410 | ||
411 | pioctlToken->at_type = token->type; | |
412 | ||
413 | switch (token->type) { | |
414 | case RX_SECIDX_KAD: | |
415 | code = rxkad_extractTokenForPioctl(token, pioctlToken); | |
416 | break; | |
417 | default: | |
418 | code = EINVAL;; | |
419 | } | |
420 | ||
421 | if (code) | |
422 | goto out; | |
423 | ||
424 | xdrlen_create(&xdrs); | |
425 | if (!xdr_ktc_tokenUnion(&xdrs, pioctlToken)) { | |
426 | code = EINVAL; | |
427 | xdr_destroy(&xdrs); | |
428 | goto out; | |
429 | } | |
430 | ||
431 | opaque->token_opaque_len = xdr_getpos(&xdrs); | |
432 | xdr_destroy(&xdrs); | |
433 | ||
434 | opaque->token_opaque_val = osi_Alloc(opaque->token_opaque_len); | |
435 | if (opaque->token_opaque_val == NULL) { | |
436 | code = ENOMEM; | |
437 | goto out; | |
438 | } | |
439 | ||
440 | xdrmem_create(&xdrs, | |
441 | opaque->token_opaque_val, | |
442 | opaque->token_opaque_len, | |
443 | XDR_ENCODE); | |
444 | if (!xdr_ktc_tokenUnion(&xdrs, pioctlToken)) { | |
445 | code = EINVAL; | |
446 | xdr_destroy(&xdrs); | |
447 | goto out; | |
448 | } | |
449 | xdr_destroy(&xdrs); | |
450 | ||
451 | out: | |
452 | xdr_free((xdrproc_t) xdr_ktc_tokenUnion, &pioctlToken); | |
453 | osi_Free(pioctlToken, sizeof(struct ktc_tokenUnion)); | |
454 | ||
455 | if (code != 0) { | |
456 | if (opaque->token_opaque_val != NULL) | |
457 | osi_Free(opaque->token_opaque_val, opaque->token_opaque_len); | |
458 | opaque->token_opaque_val = NULL; | |
459 | opaque->token_opaque_len = 0; | |
460 | } | |
461 | return code; | |
462 | } | |
463 | ||
464 | int | |
465 | afs_ExtractTokensForPioctl(struct tokenJar *token, | |
466 | time_t now, | |
467 | struct ktc_setTokenData *tokenSet) | |
468 | { | |
469 | int numTokens, pos; | |
470 | int code = 0; | |
471 | ||
472 | numTokens = countValidTokens(token, now); | |
473 | ||
474 | tokenSet->tokens.tokens_len = numTokens; | |
475 | tokenSet->tokens.tokens_val | |
476 | = xdr_alloc(sizeof(struct token_opaque) * numTokens); | |
477 | ||
478 | if (tokenSet->tokens.tokens_val == NULL) | |
479 | return ENOMEM; | |
480 | ||
481 | pos = 0; | |
482 | while (token != NULL && pos < numTokens) { | |
483 | code = extractPioctlToken(token, &tokenSet->tokens.tokens_val[pos]); | |
484 | if (code) | |
485 | goto out; | |
486 | token = token->next; | |
487 | pos++; | |
488 | } | |
489 | ||
490 | out: | |
491 | if (code) | |
492 | xdr_free((xdrproc_t) xdr_ktc_setTokenData, tokenSet); | |
493 | ||
494 | return code; | |
495 | } |