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