52d65fa7493e0998be61af13b456442250bbc0af
[openafs.git] / src / rxkad / ticket.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <afs/stds.h>
13
14 #include <roken.h>
15
16 #include <rx/xdr.h>
17 #include <rx/rx.h>
18
19 #include "lifetimes.h"
20 #include "rxkad.h"
21 #include "rxkad_convert.h"
22
23 /* This union is used to insure we allocate enough space for a key
24  * schedule even if we are linked against a library that uses OpenSSL's
25  * larger representation.  This is necessary so we don't lose if an
26  * application uses both rxkad and openssl.
27  */
28 union Key_schedule_safe {
29     DES_key_schedule schedule;
30     struct {
31         union {
32             char cblock[8];
33             long deslong[2];
34         } ks;
35         int weak_key;
36     } openssl_schedule[16];
37 };
38
39 #define getstr(name,min) \
40     slen = strlen(ticket); \
41     if ((slen < min) || (slen >= MAXKTCNAMELEN)) return -1; \
42     strcpy (name, ticket); \
43     ticket += slen+1
44
45 static int
46 decode_athena_ticket(char *ticket, int ticketLen, char *name, char *inst,
47                      char *realm, afs_int32 * host,
48                      struct ktc_encryptionKey *sessionKey, afs_uint32 * start,
49                      afs_uint32 * end)
50 {
51     char *ticketBeg = ticket;
52     char flags;
53     int slen;
54     int tlen;
55     unsigned char lifetime;
56     char sname[MAXKTCNAMELEN];  /* these aren't used, */
57     char sinst[MAXKTCNAMELEN];  /* but are in the ticket */
58
59     flags = *ticket++;
60     getstr(name, 1);
61     getstr(inst, 0);
62     getstr(realm, 0);
63
64     memcpy(host, ticket, sizeof(*host));
65     ticket += sizeof(*host);
66     *host = ktohl(flags, *host);
67
68     memcpy(sessionKey, ticket, sizeof(struct ktc_encryptionKey));
69     ticket += sizeof(struct ktc_encryptionKey);
70
71     lifetime = *ticket++;
72     memcpy(start, ticket, sizeof(*start));
73     ticket += sizeof(*start);
74     *start = ktohl(flags, *start);
75     *end = life_to_time(*start, lifetime);
76
77     getstr(sname, 1);
78     getstr(sinst, 0);
79
80     tlen = ticket - ticketBeg;
81     if ((round_up_to_ebs(tlen) != ticketLen) && (ticketLen != 56))
82         return -1;
83     return 0;
84 }
85
86 /* This is called to interpret a ticket.  It is assumed that the necessary keys
87    have been added so that the key version number in the ticket will indicate a
88    valid key for decrypting the ticket.  The various fields inside the ticket
89    are copied into the return arguments.  An error code indicate some problem
90    interpreting the ticket and the values of the output parameters are
91    undefined. */
92
93 int
94 tkt_DecodeTicket(char *asecret, afs_int32 ticketLen,
95                  struct ktc_encryptionKey *key, char *name, char *inst,
96                  char *cell, struct ktc_encryptionKey *sessionKey, afs_int32 * host,
97                  afs_uint32 * start, afs_uint32 * end)
98 {
99     char clear_ticket[MAXKTCTICKETLEN];
100     char *ticket;
101     union Key_schedule_safe schedule;
102     int code;
103
104     if (ticketLen == 0)
105         return RXKADBADTICKET;  /* no ticket */
106     if ((ticketLen < MINKTCTICKETLEN) ||        /* minimum legal ticket size */
107         (ticketLen > MAXKTCTICKETLEN) ||        /* maximum legal ticket size */
108         ((ticketLen) % 8 != 0)) /* enc. part must be (0 mod 8) bytes */
109         return RXKADBADTICKET;
110
111     if (DES_key_sched(ktc_to_cblock(key), &schedule.schedule))
112         return RXKADBADKEY;
113
114     ticket = clear_ticket;
115     DES_pcbc_encrypt(asecret, ticket, ticketLen, &schedule.schedule, ktc_to_cblockptr(key), DECRYPT);
116
117     code =
118         decode_athena_ticket(ticket, ticketLen, name, inst, cell, host,
119                              (struct ktc_encryptionKey *)sessionKey, start, end);
120
121     if (code)
122         return RXKADBADTICKET;
123
124     code = tkt_CheckTimes(*start, *end, time(0));
125     if (code == 0)
126         return RXKADNOAUTH;
127     else if (code == -1)
128         return RXKADEXPIRED;
129     else if (code < -1)
130         return RXKADBADTICKET;
131
132     return 0;
133 }
134
135 /* This makes a Kerberos ticket */
136 /*
137   char          *ticket;                * ticket is constructed here *
138   int           *ticketLen;             * output length of finished ticket *
139   struct ktc_encryptionKey *key;        * key ticket should be sealed with *
140   char          *name;                  * user of this ticket *
141   char          *inst;
142   char          *cell;                  * cell of authentication *
143   afs_uint32     start,end;             * life of ticket *
144   struct ktc_encryptionKey *sessionKey; * session key invented for ticket *
145   afs_uint32     host;                  * caller's host address *
146   char          *sname;                 * server *
147   char          *sinst;
148 */
149
150 #define putstr(name,min) \
151     slen = strlen(name); \
152     if ((slen < min) || (slen >= MAXKTCNAMELEN)) return -1; \
153     strcpy (ticket, name); \
154     ticket += slen+1
155 #define putint(num) num = htonl(num);\
156                     memcpy(ticket, &num, sizeof(num));\
157                     ticket += sizeof(num)
158
159 static int
160 assemble_athena_ticket(char *ticket, int *ticketLen, char *name, char *inst,
161                        char *realm, afs_int32 host,
162                        struct ktc_encryptionKey *sessionKey, afs_uint32 start,
163                        afs_uint32 end, char *sname, char *sinst)
164 {
165     char *ticketBeg = ticket;
166     int slen;
167     unsigned char life;
168
169     *ticket++ = 0;              /* flags, always send net-byte-order */
170     putstr(name, 1);
171     putstr(inst, 0);
172     putstr(realm, 0);
173     putint(host);
174
175     memcpy(ticket, sessionKey, sizeof(struct ktc_encryptionKey));
176     ticket += sizeof(struct ktc_encryptionKey);
177
178     life = time_to_life(start, end);
179     if (life == 0)
180         return -1;
181     *ticket++ = life;
182
183     putint(start);
184     putstr(sname, 1);
185     putstr(sinst, 0);
186
187     *ticketLen = ticket - ticketBeg;
188     return 0;
189 }
190
191 int
192 tkt_MakeTicket(char *ticket, int *ticketLen, struct ktc_encryptionKey *key,
193                char *name, char *inst, char *cell, afs_uint32 start,
194                afs_uint32 end, struct ktc_encryptionKey *sessionKey,
195                afs_uint32 host, char *sname, char *sinst)
196 {
197     int code;
198     union Key_schedule_safe schedule;
199
200     *ticketLen = 0;             /* in case we return early */
201     code =
202         assemble_athena_ticket(ticket, ticketLen, name, inst, cell, host,
203                                sessionKey, start, end, sname, sinst);
204     *ticketLen = round_up_to_ebs(*ticketLen);   /* round up */
205     if (code)
206         return -1;
207
208     /* encrypt ticket */
209     if ((code = DES_key_sched(ktc_to_cblock(key), &schedule.schedule))) {
210         printf("In tkt_MakeTicket: key_sched returned %d\n", code);
211         return RXKADBADKEY;
212     }
213     DES_pcbc_encrypt(ticket, ticket, *ticketLen, &schedule.schedule,
214                      ktc_to_cblockptr(key), ENCRYPT);
215     return 0;
216 }
217
218 /* This is just a routine that checks the consistency of ticket lifetimes.  It
219    returns three values: */
220 /* -2 means the times are inconsistent or ticket has expired
221    -1 means the ticket has recently expired.
222     0 means the times are consistent but start time is in the (near) future.
223     1 means the start time is in the past and the end time is infinity.
224     2 means the start time is past and the end time is in the future
225             and the lifetime is within the legal limit.
226  */
227
228 int
229 tkt_CheckTimes(afs_uint32 start, afs_uint32 end, afs_uint32 now)
230 {
231     int active;
232
233     if (start >= end)
234         return -2;              /* zero or negative lifetime */
235     if (start > now + KTC_TIME_UNCERTAINTY + MAXKTCTICKETLIFETIME)
236         return -2;              /* starts too far in the future? */
237     if ((start != 0) && (end != NEVERDATE)
238         && (end - start > MAXKTCTICKETLIFETIME))
239         return -2;              /* too long a life */
240     if ((end != NEVERDATE) && (end + KTC_TIME_UNCERTAINTY < now)) {     /* expired */
241         if ((start != 0)
242             && (now - start > MAXKTCTICKETLIFETIME + 24 * 60 * 60))
243             return -2;
244         else
245             return -1;          /* expired only recently */
246     }
247     if ((start == 0) || (start - KTC_TIME_UNCERTAINTY <= now))
248         active = 1;
249     else
250         active = 0;             /* start time not yet arrived */
251
252     if ((start == 0) || (end == NEVERDATE))
253         return active;          /* no expiration time */
254     return active * 2;          /* ticket valid */
255 }
256
257 afs_int32
258 ktohl(char flags, afs_int32 l)
259 {
260     if (flags & 1) {
261         unsigned char *lp = (unsigned char *)&l;
262         afs_int32 hl;
263         hl = *lp + (*(lp + 1) << 8) + (*(lp + 2) << 16) + (*(lp + 3) << 24);
264         return hl;
265     }
266     return ntohl(l);
267 }
268
269 /* life_to_time - takes a start time and a Kerberos standard lifetime char and
270  * returns the corresponding end time.  There are four simple cases to be
271  * handled.  The first is a life of 0xff, meaning no expiration, and results in
272  * an end time of 0xffffffff.  The second is when life is less than the values
273  * covered by the table.  In this case, the end time is the start time plus the
274  * number of 5 minute intervals specified by life.  The third case returns
275  * start plus the MAXTKTLIFETIME if life is greater than TKTLIFEMAXFIXED.  The
276  * last case, uses the life value (minus TKTLIFEMINFIXED) as an index into the
277  * table to extract the lifetime in seconds, which is added to start to produce
278  * the end time. */
279
280 afs_uint32
281 life_to_time(afs_uint32 start, unsigned char life)
282 {
283     int realLife;
284
285     if (life == TKTLIFENOEXPIRE)
286         return NEVERDATE;
287     if (life < TKTLIFEMINFIXED)
288         return start + life * 5 * 60;
289     if (life > TKTLIFEMAXFIXED)
290         return start + MAXTKTLIFETIME;
291     realLife = tkt_lifetimes[life - TKTLIFEMINFIXED];
292     return start + realLife;
293 }
294
295 /* time_to_life - takes start and end times for the ticket and returns a
296  * Kerberos standard lifetime char possibily using the tkt_lifetimes table for
297  * lifetimes above 127*5minutes.  First, the special case of (end ==
298  * 0xffffffff) is handled to mean no expiration.  Then negative lifetimes and
299  * those greater than the maximum ticket lifetime are rejected.  Then lifetimes
300  * less than the first table entry are handled by rounding the requested
301  * lifetime *up* to the next 5 minute interval.  The final step is to search
302  * the table for the smallest entry *greater than or equal* to the requested
303  * entry.  The actual code is prepared to handle the case where the table is
304  * unordered but that it an unnecessary frill. */
305
306 unsigned char
307 time_to_life(afs_uint32 start, afs_uint32 end)
308 {
309     int lifetime = end - start;
310     int best, best_i;
311     int i;
312
313     if (end == NEVERDATE)
314         return TKTLIFENOEXPIRE;
315     if ((lifetime > MAXKTCTICKETLIFETIME) || (lifetime <= 0))
316         return 0;
317     if (lifetime < tkt_lifetimes[0])
318         return (lifetime + 5 * 60 - 1) / (5 * 60);
319     best_i = -1;
320     best = MAXKTCTICKETLIFETIME;
321     for (i = 0; i < TKTLIFENUMFIXED; i++)
322         if (tkt_lifetimes[i] >= lifetime) {
323             int diff = tkt_lifetimes[i] - lifetime;
324             if (diff < best) {
325                 best = diff;
326                 best_i = i;
327             }
328         }
329     if (best_i < 0)
330         return 0;
331     return best_i + TKTLIFEMINFIXED;
332 }