Commit | Line | Data |
---|---|---|
805e021f CE |
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 | } |