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