3e884f25608e0ba23b25f19eb6740256212cd083
[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 <afs/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 void
79 freeToken(struct ktc_tokenUnion *token) {
80     xdr_free((xdrproc_t)xdr_ktc_tokenUnion, token);
81 }
82
83 static int
84 rxkadTokenEqual(struct ktc_tokenUnion *tokenA, struct ktc_tokenUnion *tokenB) {
85     return (tokenA->ktc_tokenUnion_u.at_kad.rk_kvno ==
86             tokenB->ktc_tokenUnion_u.at_kad.rk_kvno
87          && tokenA->ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_len ==
88             tokenB->ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_len
89          && !memcmp(tokenA->ktc_tokenUnion_u.at_kad.rk_key,
90                     tokenB->ktc_tokenUnion_u.at_kad.rk_key, 8)
91          && !memcmp(tokenA->ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_val,
92                     tokenB->ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_val,
93                     tokenA->ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_len));
94 }
95
96 static int
97 tokenEqual(struct ktc_tokenUnion *tokenA,
98            struct ktc_tokenUnion *tokenB) {
99     switch (tokenA->at_type) {
100       case AFSTOKEN_UNION_KAD:
101         return rxkadTokenEqual(tokenA, tokenB);
102     }
103     return 0;
104 }
105
106 static int
107 rawTokenEqual(struct token_opaque *tokenA, struct token_opaque *tokenB) {
108     return (tokenA->token_opaque_len == tokenB->token_opaque_len &&
109             !memcmp(tokenA->token_opaque_val, tokenB->token_opaque_val,
110                     tokenA->token_opaque_len));
111 }
112
113 /* Given a token type, return the entry number of the first token of that
114  * type */
115 static int
116 findTokenEntry(struct ktc_setTokenData *token,
117                int targetType)
118 {
119     int i;
120
121     for (i = 0; i < token->tokens.tokens_len; i++) {
122         if (tokenType(&token->tokens.tokens_val[i]) == targetType)
123             return i;
124     }
125     return -1;
126 }
127
128 /* XDR encode a token union structure, and return data and length information
129  * suitable for stuffing into a token_opaque structure
130  */
131 static int
132 encodeTokenUnion(struct ktc_tokenUnion *token,
133                  char **dataPtr, size_t *lenPtr) {
134     char *data = NULL;
135     size_t len;
136     XDR xdrs;
137     int code = 0;
138
139     *dataPtr = NULL;
140     *lenPtr = 0;
141
142     xdrlen_create(&xdrs);
143     if (!xdr_ktc_tokenUnion(&xdrs, token)) {
144         code = EINVAL;
145         goto out;
146     }
147
148     len = xdr_getpos(&xdrs);
149     data = malloc(len);
150     if (data == NULL) {
151         code = ENOMEM;
152         goto out;
153     }
154     xdr_destroy(&xdrs);
155
156     xdrmem_create(&xdrs, data, len, XDR_ENCODE);
157     if (!xdr_ktc_tokenUnion(&xdrs, token)) {
158         code = EINVAL;
159         goto out;
160     }
161
162     *dataPtr = data;
163     *lenPtr = len;
164
165 out:
166     xdr_destroy(&xdrs);
167     if (code) {
168         if (data)
169             free(data);
170     }
171
172     return code;
173 }
174
175 static void
176 addOpaque(struct ktc_setTokenData *jar, char *data, size_t len)
177 {
178     int entry;
179
180     entry = jar->tokens.tokens_len;
181     jar->tokens.tokens_val = realloc(jar->tokens.tokens_val,
182                                      entry + 1 * sizeof(token_opaque));
183     jar->tokens.tokens_len++;
184     jar->tokens.tokens_val[entry].token_opaque_val = data;
185     jar->tokens.tokens_val[entry].token_opaque_len = len;
186 }
187
188 /*!
189  * Extract a specific token element from a unified token structure
190  *
191  * This routine extracts an afsTokenUnion structure from the tokenData
192  * structure used by the SetTokenEx and GetTokenEx pioctls
193  *
194  * @param[in] token
195  *      A ktc_setTokenData structure containing the token to extract from
196  * @param[in] targetType
197  *      The securityClass index of the token to be extracted
198  * @param[out] output
199  *      The decoded token. On entry, this must point to a block of memory
200  *      of sufficient size to contain an afsTokenUnion structure. Upon
201  *      completion, this block must be passed to xdr_free(), using the
202  *      xdr_afsTokenUnion xdrproc_t.
203  */
204 int
205 token_findByType(struct ktc_setTokenData *token,
206                  int targetType,
207                  struct ktc_tokenUnion *output)
208 {
209     int entry;
210
211     memset(output, 0, sizeof *output);
212     entry = findTokenEntry(token, targetType);
213     if (entry == -1)
214         return EINVAL;
215
216     if (!decodeToken(&token->tokens.tokens_val[entry], output))
217         return EINVAL;
218
219     if (output->at_type != targetType) {
220         xdr_free((xdrproc_t)xdr_ktc_tokenUnion, output);
221         return EINVAL;
222     }
223
224     return 0;
225 }
226
227 /*!
228  * Given an unified token, populate an rxkad token from it
229  *
230  * This routine populates an rxkad token using information contained
231  * in the tokenData structure used by the SetTokenEx and GetTokenEX
232  * pioctls.
233  *
234  * @param[in] token
235  *      The new format token to extract information from.
236  * @param[out] rxkadToken
237  *      The old-style rxkad token. This must be a pointer to an existing
238  *      data block of sufficient size
239  * @param[out] flags
240  *      The set of token flags
241  * @param[out] aclient
242  *      The client owning the token. This must be a pointer to an existing
243  *      data block of sufficient size, or NULL.
244  */
245
246 int
247 token_extractRxkad(struct ktc_setTokenData *token,
248                    struct ktc_token *rxkadToken,
249                    int *flags,
250                    struct ktc_principal *aclient)
251 {
252     struct ktc_tokenUnion uToken;
253     int code;
254
255     memset(&uToken, 0, sizeof(uToken));
256     if (aclient)
257         memset(aclient, 0, sizeof(*aclient));
258
259     code = token_findByType(token, AFSTOKEN_UNION_KAD, &uToken);
260     if (code)
261         return code;
262
263     rxkadToken->kvno = uToken.ktc_tokenUnion_u.at_kad.rk_kvno;
264     memcpy(rxkadToken->sessionKey.data,
265            uToken.ktc_tokenUnion_u.at_kad.rk_key, 8);
266     rxkadToken->startTime = uToken.ktc_tokenUnion_u.at_kad.rk_begintime;
267     rxkadToken->endTime   = uToken.ktc_tokenUnion_u.at_kad.rk_endtime;
268     rxkadToken->ticketLen = uToken.ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_len;
269
270     if (rxkadToken->ticketLen > MAXKTCTICKETLEN) {
271         code = E2BIG;
272         goto out;
273     }
274
275     memcpy(rxkadToken->ticket,
276            uToken.ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_val,
277            rxkadToken->ticketLen);
278
279     if (flags)
280         *flags = uToken.ktc_tokenUnion_u.at_kad.rk_primary_flag & ~0x8000;
281
282     if (aclient) {
283         strncpy(aclient->cell, token->cell, MAXKTCREALMLEN-1);
284         aclient->cell[MAXKTCREALMLEN-1] = '\0';
285
286         if ((rxkadToken->kvno == 999) ||        /* old style bcrypt ticket */
287             (rxkadToken->startTime &&   /* new w/ prserver lookup */
288              (((rxkadToken->endTime - rxkadToken->startTime) & 1) == 1))) {
289             sprintf(aclient->name, "AFS ID %d",
290                     uToken.ktc_tokenUnion_u.at_kad.rk_viceid);
291         } else {
292             sprintf(aclient->name, "Unix UID %d",
293                     uToken.ktc_tokenUnion_u.at_kad.rk_viceid);
294         }
295     }
296
297 out:
298     xdr_free((xdrproc_t) xdr_ktc_tokenUnion, &uToken);
299     return code;
300 }
301
302 struct ktc_setTokenData *
303 token_buildTokenJar(char * cellname) {
304     struct ktc_setTokenData *jar;
305
306     jar = malloc(sizeof(struct ktc_setTokenData));
307     if (jar == NULL)
308         return NULL;
309
310     memset(jar, 0, sizeof(struct ktc_setTokenData));
311
312     jar->cell = strdup(cellname);
313
314     return jar;
315 }
316
317 /*!
318  * Add a token to an existing set of tokens. This will always add the token,
319  * regardless of whether an entry for the security class already exists
320  */
321 int
322 token_addToken(struct ktc_setTokenData *jar, struct ktc_tokenUnion *token) {
323     int code;
324     char *data;
325     size_t len;
326
327     code = encodeTokenUnion(token, &data, &len);
328     if (code)
329         goto out;
330
331     addOpaque(jar, data, len);
332
333 out:
334     return code;
335 }
336
337 /*!
338  * Replace at token in an existing set of tokens. This replaces the first
339  * token stored of a matching type. If no matching tokens are found, then
340  * the new token is added at the end of the list
341  */
342 int
343 token_replaceToken(struct ktc_setTokenData *jar,
344                    struct ktc_tokenUnion *token) {
345     int entry;
346     char *data;
347     size_t len;
348     int code;
349
350     entry = findTokenEntry(jar, token->at_type);
351     if (entry == -1)
352         return token_addToken(jar, token);
353
354     code = encodeTokenUnion(token, &data, &len);
355     if (code)
356         goto out;
357
358     free(jar->tokens.tokens_val[entry].token_opaque_val);
359     jar->tokens.tokens_val[entry].token_opaque_val = data;
360     jar->tokens.tokens_val[entry].token_opaque_len = len;
361
362 out:
363     return code;
364 }
365
366 /*!
367  * Work out if a pair of token sets are equivalent. Equivalence
368  * is defined as both sets containing the same number of tokens,
369  * and every token in the first set having an equivalent token
370  * in the second set. Cell name and flags value are not compared.
371  *
372  * @param[in] tokensA
373  *      First set of tokens
374  * @param[in] tokensB
375  *      Second set of tokens
376  *
377  * @returns
378  *      True if token sets are equivalent, false otherwise
379  */
380 int
381 token_SetsEquivalent(struct ktc_setTokenData *tokenSetA,
382                      struct ktc_setTokenData *tokenSetB) {
383     int i, j;
384     int decodedOK, found;
385     struct ktc_tokenUnion tokenA, tokenB;
386
387     if (tokenSetA->tokens.tokens_len != tokenSetB->tokens.tokens_len)
388         return 0;
389
390     for (i=0; i<tokenSetA->tokens.tokens_len; i++) {
391         found = 0;
392
393         decodedOK = decodeToken(&tokenSetA->tokens.tokens_val[i], &tokenA);
394
395         for (j=0; j<tokenSetB->tokens.tokens_len && !found; j++) {
396             if (rawTokenEqual(&tokenSetA->tokens.tokens_val[i],
397                               &tokenSetB->tokens.tokens_val[j])) {
398                 found = 1;
399                 break;
400             }
401
402             if (decodedOK &&
403                 tokenType(&tokenSetB->tokens.tokens_val[j]) == tokenA.at_type
404                 && decodeToken(&tokenSetB->tokens.tokens_val[j], &tokenB)) {
405
406                 if (tokenEqual(&tokenA, &tokenB)) {
407                     found = 1;
408                     break;
409                 }
410                 freeToken(&tokenB);
411             }
412         }
413         if (decodedOK)
414             freeToken(&tokenA);
415
416         if (!found)
417             return 0;
418     }
419     /* If we made it this far without exiting, we must have found equivalents
420      * for all of our tokens */
421     return 1;
422 }
423
424 void
425 token_setPag(struct ktc_setTokenData *jar, int setpag) {
426     if (setpag)
427         jar->flags |= AFSTOKEN_EX_SETPAG;
428     else
429         jar->flags &= ~AFSTOKEN_EX_SETPAG;
430 }
431
432 void
433 token_FreeSet(struct ktc_setTokenData **jar) {
434     if (*jar) {
435         xdr_free((xdrproc_t)xdr_ktc_setTokenData, *jar);
436         memset(*jar, 0, sizeof(struct ktc_setTokenData));
437         *jar = NULL;
438     }
439 }