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