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