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