Add rxgk support to userok
[openafs.git] / src / auth / token.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
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 }