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