rxkad-rt34568-20060629
[openafs.git] / src / rxkad / ticket5.c
1 /*
2  * Copyright (c) 1995, 1996, 1997, 2002 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  * 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 /*
34  * Copyright 1992, 2002 by the Massachusetts Institute of Technology.
35  * All Rights Reserved.
36  *
37  * Export of this software from the United States of America may
38  *   require a specific license from the United States Government.
39  *   It is the responsibility of any person or organization contemplating
40  *   export to obtain such a license before exporting.
41  * 
42  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
43  * distribute this software and its documentation for any purpose and
44  * without fee is hereby granted, provided that the above copyright
45  * notice appear in all copies and that both that copyright notice and
46  * this permission notice appear in supporting documentation, and that
47  * the name of M.I.T. not be used in advertising or publicity pertaining
48  * to distribution of the software without specific, written prior
49  * permission.  Furthermore if you modify this software you must label
50  * your software as modified software and not distribute it in such a
51  * fashion that it might be confused with the original M.I.T. software.
52  * M.I.T. makes no representations about the suitability of
53  * this software for any purpose.  It is provided "as is" without express
54  * or implied warranty.
55  */
56
57 #include <afsconfig.h>
58 #if defined(UKERNEL)
59 #include "../afs/param.h"
60 #else
61 #include <afs/param.h>
62 #endif
63
64 RCSID
65     ("$Header$");
66
67 #if defined(UKERNEL)
68 #include "../afs/sysincludes.h"
69 #include "../afs/afsincludes.h"
70 #include "../afs/stds.h"
71 #include "../rx/xdr.h"
72 #include "../rx/rx.h"
73 #include "../des/des.h"
74 #include "../afs/lifetimes.h"
75 #include "../afs/rxkad.h"
76 #else /* defined(UKERNEL) */
77 #include <afs/stds.h>
78 #include <sys/types.h>
79 #ifdef AFS_NT40_ENV
80 #include <winsock2.h>
81 #else
82 #include <netinet/in.h>
83 #endif
84 #ifdef HAVE_STRING_H
85 #include <string.h>
86 #else
87 #ifdef HAVE_STRINGS_H
88 #include <strings.h>
89 #endif
90 #endif
91 #include <rx/xdr.h>
92 #include <rx/rx.h>
93 #include <des.h>
94 #include "lifetimes.h"
95 #include "rxkad.h"
96 #endif /* defined(UKERNEL) */
97
98 #include "v5gen-rewrite.h"
99 #include "asn1-common.h"
100 #include "der.h"
101 #include "v5gen.h"
102 #include "v5der.c"
103 #include "v5gen.c"
104 #include "md4.h"
105 #include "md5.h"
106
107 /*
108  * Principal conversion Taken from src/lib/krb5/krb/conv_princ from MIT Kerberos.  If you
109  * find a need to change the services here, please consider opening a
110  * bug with MIT by sending mail to krb5-bugs@mit.edu.
111  */
112
113 struct krb_convert {
114     char *v4_str;
115     char *v5_str;
116     unsigned int flags;
117     unsigned int len;
118 };
119
120 #define DO_REALM_CONVERSION 0x00000001
121
122 /*
123  * Kadmin doesn't do realm conversion because it's currently
124  * kadmin/REALM.NAME.  Zephyr doesn't because it's just zephyr/zephyr.
125  *
126  * "Realm conversion" is a bit of a misnomer; really, the v5 name is
127  * using a FQDN or something that looks like it, where the v4 name is
128  * just using the first label.  Sometimes that second principal name
129  * component is a hostname, sometimes the realm name, sometimes it's
130  * neither.
131  *
132  * This list should probably be more configurable, and more than
133  * likely on a per-realm basis, so locally-defined services can be
134  * added, or not.
135  */
136 static const struct krb_convert sconv_list[] = {
137     /* Realm conversion, Change service name */
138 #define RC(V5NAME,V4NAME) { V5NAME, V4NAME, DO_REALM_CONVERSION, sizeof(V5NAME)-1 }
139     /* Realm conversion */
140 #define R(NAME)         { NAME, NAME, DO_REALM_CONVERSION, sizeof(NAME)-1 }
141     /* No Realm conversion */
142 #define NR(NAME)        { NAME, NAME, 0, sizeof(NAME)-1 }
143
144     NR("kadmin"),
145     RC("rcmd", "host"),
146     R("discuss"),
147     R("rvdsrv"),
148     R("sample"),
149     R("olc"),
150     R("pop"),
151     R("sis"),
152     R("rfs"),
153     R("imap"),
154     R("ftp"),
155     R("ecat"),
156     R("daemon"),
157     R("gnats"),
158     R("moira"),
159     R("prms"),
160     R("mandarin"),
161     R("register"),
162     R("changepw"),
163     R("sms"),
164     R("afpserver"),
165     R("gdss"),
166     R("news"),
167     R("abs"),
168     R("nfs"),
169     R("tftp"),
170     NR("zephyr"),
171     R("http"),
172     R("khttp"),
173     R("pgpsigner"),
174     R("irc"),
175     R("mandarin-agent"),
176     R("write"),
177     R("palladium"),
178     R("imap"),
179     R("smtp"),
180     R("lmtp"),
181     R("ldap"),
182     R("acap"),
183     R("argus"),
184     R("mupdate"),
185     R("argus"),
186     {0, 0, 0, 0},
187 #undef R
188 #undef RC
189 #undef NR
190 };
191
192 static int
193   krb5_des_decrypt(struct ktc_encryptionKey *, int, void *, size_t, void *,
194                    size_t *);
195
196
197
198
199 int
200 tkt_DecodeTicket5(char *ticket, afs_int32 ticket_len,
201                   int (*get_key) (char *, int, struct ktc_encryptionKey *),
202                   char *get_key_rock, int serv_kvno, char *name, char *inst,
203                   char *cell, char *session_key, afs_int32 * host,
204                   afs_int32 * start, afs_int32 * end)
205 {
206     char plain[MAXKRB5TICKETLEN];
207     struct ktc_encryptionKey serv_key;
208     Ticket t5;                  /* Must free */
209     EncTicketPart decr_part;    /* Must free */
210     int code;
211     size_t siz, plainsiz;
212     int v5_serv_kvno;
213     char *v5_comp0, *v5_comp1, *c;
214     const struct krb_convert *p;
215
216     memset(&t5, 0, sizeof(t5));
217     memset(&decr_part, 0, sizeof(decr_part));
218
219     *host = 0;
220
221     if (ticket_len == 0)
222         return RXKADBADTICKET;  /* no ticket */
223
224     if (serv_kvno == RXKAD_TKT_TYPE_KERBEROS_V5) {
225         code = decode_Ticket(ticket, ticket_len, &t5, &siz);
226         if (code != 0)
227             goto cleanup;
228
229         if (t5.tkt_vno != 5)
230             goto bad_ticket;
231     } else {
232         code = decode_EncryptedData(ticket, ticket_len, &t5.enc_part, &siz);
233         if (code != 0)
234             goto cleanup;
235     }
236
237     /* If kvno is null, it's probably not included because it was kvno==0 
238      * in the ticket */
239     if (t5.enc_part.kvno == NULL) {
240         v5_serv_kvno = 0;
241     } else {
242         v5_serv_kvno = *t5.enc_part.kvno;
243     }
244
245     /* Check that the key type really fit into 8 bytes */
246     switch (t5.enc_part.etype) {
247     case ETYPE_DES_CBC_CRC:
248     case ETYPE_DES_CBC_MD4:
249     case ETYPE_DES_CBC_MD5:
250         break;
251     default:
252         goto unknown_key;
253     }
254
255     /* check ticket */
256     if (t5.enc_part.cipher.length > sizeof(plain)
257         || t5.enc_part.cipher.length % 8 != 0)
258         goto bad_ticket;
259
260     code = (*get_key) (get_key_rock, v5_serv_kvno, &serv_key);
261     if (code)
262         goto unknown_key;
263
264     /* Decrypt data here, save in plain, assume it will shrink */
265     code =
266         krb5_des_decrypt(&serv_key, t5.enc_part.etype,
267                          t5.enc_part.cipher.data, t5.enc_part.cipher.length,
268                          plain, &plainsiz);
269     if (code != 0)
270         goto bad_ticket;
271
272     /* Decode ticket */
273     code = decode_EncTicketPart(plain, plainsiz, &decr_part, &siz);
274     if (code != 0)
275         goto bad_ticket;
276
277     /* Extract realm and principal */
278     strncpy(cell, decr_part.crealm, MAXKTCNAMELEN);
279     cell[MAXKTCNAMELEN - 1] = '\0';
280     inst[0] = '\0';
281     switch (decr_part.cname.name_string.len) {
282     case 2:
283         v5_comp0 = decr_part.cname.name_string.val[0];
284         v5_comp1 = decr_part.cname.name_string.val[1];
285         p = sconv_list;
286         while (p->v4_str) {
287             if (strcmp(p->v5_str, v5_comp0) == 0) {
288                 /*
289                  * It is, so set the new name now, and chop off
290                  * instance's domain name if requested.
291                  */
292                 strncpy(name, p->v4_str, MAXKTCNAMELEN);
293                 name[MAXKTCNAMELEN - 1] = '\0';
294                 if (p->flags & DO_REALM_CONVERSION) {
295                     c = strchr(v5_comp1, '.');
296                     if (!c || (c - v5_comp1) >= MAXKTCNAMELEN - 1)
297                         goto bad_ticket;
298                     strncpy(inst, v5_comp1, c - v5_comp1);
299                     inst[c - v5_comp1] = '\0';
300                 }
301                 break;
302             }
303             p++;
304         }
305
306         if (!p->v4_str) {
307             strncpy(inst, decr_part.cname.name_string.val[1], MAXKTCNAMELEN);
308             inst[MAXKTCNAMELEN - 1] = '\0';
309             strncpy(name, decr_part.cname.name_string.val[0], MAXKTCNAMELEN);
310             name[MAXKTCNAMELEN - 1] = '\0';
311         }
312         break;
313     case 1:
314         strncpy(name, decr_part.cname.name_string.val[0], MAXKTCNAMELEN);
315         name[MAXKTCNAMELEN - 1] = '\0';
316         break;
317     default:
318         goto bad_ticket;
319     }
320
321     /* 
322      * If the first part of the name_string contains a dot, punt since
323      * then we can't see the diffrence between the kerberos 5
324      * principals foo.root and foo/root later in the fileserver.
325      */
326     if (strchr(decr_part.cname.name_string.val[0], '.') != NULL)
327         goto bad_ticket;
328
329     /* Verify that decr_part.key is of right type */
330     switch (decr_part.key.keytype) {
331     case ETYPE_DES_CBC_CRC:
332     case ETYPE_DES_CBC_MD4:
333     case ETYPE_DES_CBC_MD5:
334         break;
335     default:
336         goto bad_ticket;
337     }
338
339     if (decr_part.key.keyvalue.length != 8)
340         goto bad_ticket;
341
342     /* Extract session key */
343     memcpy(session_key, decr_part.key.keyvalue.data, 8);
344
345     /* Check lifetimes and host addresses, flags etc */
346     {
347         time_t now = time(0);   /* Use fast time package instead??? */
348         *start = decr_part.authtime;
349         if (decr_part.starttime)
350             *start = *decr_part.starttime;
351 #if 0
352         if (*start - now > CLOCK_SKEW || decr_part.flags.invalid)
353             goto no_auth;
354 #else
355         if (decr_part.flags.invalid)
356             goto no_auth;
357 #endif
358         if (now > decr_part.endtime)
359             goto tkt_expired;
360         *end = decr_part.endtime;
361     }
362
363   cleanup:
364     if (serv_kvno == RXKAD_TKT_TYPE_KERBEROS_V5)
365         free_Ticket(&t5);
366     else
367         free_EncryptedData(&t5.enc_part);
368     free_EncTicketPart(&decr_part);
369     memset(&serv_key, 0, sizeof(serv_key));
370     return code;
371
372   unknown_key:
373     code = RXKADUNKNOWNKEY;
374     goto cleanup;
375   no_auth:
376     code = RXKADNOAUTH;
377     goto cleanup;
378   tkt_expired:
379     code = RXKADEXPIRED;
380     goto cleanup;
381   bad_ticket:
382     code = RXKADBADTICKET;
383     goto cleanup;
384
385 }
386
387 static int
388 verify_checksum_md4(void *data, size_t len,
389                     void *cksum, size_t cksumsz,
390                     struct ktc_encryptionKey *key)
391 {
392     MD4_CTX md4;
393     unsigned char tmp[16];
394
395     MD4_Init(&md4);
396     MD4_Update(&md4, data, len);
397     MD4_Final(tmp, &md4);
398
399     if (memcmp(tmp, cksum, cksumsz) != 0)
400         return 1;
401     return 0;
402 }
403
404 static int
405 verify_checksum_md5(void *data, size_t len,
406                     void *cksum, size_t cksumsz,
407                     struct ktc_encryptionKey *key)
408 {
409     MD5_CTX md5;
410     unsigned char tmp[16];
411
412     MD5_Init(&md5);
413     MD5_Update(&md5, data, len);
414     MD5_Final(tmp, &md5);
415
416     if (memcmp(tmp, cksum, cksumsz) != 0)
417         return 1;
418     return 0;
419 }
420
421 static int
422 verify_checksum_crc(void *data, size_t len, void *cksum, size_t cksumsz,
423                     struct ktc_encryptionKey *key)
424 {
425     afs_uint32 crc;
426     char r[4];
427
428     _rxkad_crc_init_table();
429     crc = _rxkad_crc_update(data, len, 0);
430     r[0] = crc & 0xff;
431     r[1] = (crc >> 8) & 0xff;
432     r[2] = (crc >> 16) & 0xff;
433     r[3] = (crc >> 24) & 0xff;
434
435     if (memcmp(cksum, r, 4) != 0)
436         return 1;
437     return 0;
438 }
439
440
441 static int
442 krb5_des_decrypt(struct ktc_encryptionKey *key, int etype, void *in,
443                  size_t insz, void *out, size_t * outsz)
444 {
445     int (*cksum_func) (void *, size_t, void *, size_t,
446                        struct ktc_encryptionKey *);
447     des_cblock ivec;
448     des_key_schedule s;
449     char cksum[24];
450     size_t cksumsz;
451     int ret = 1;                /* failure */
452
453     cksum_func = NULL;
454
455     des_key_sched(key, &s);
456
457 #define CONFOUNDERSZ 8
458
459     switch (etype) {
460     case ETYPE_DES_CBC_CRC:
461         memcpy(&ivec, key, sizeof(ivec));
462         cksumsz = 4;
463         cksum_func = verify_checksum_crc;
464         break;
465     case ETYPE_DES_CBC_MD4:
466         memset(&ivec, 0, sizeof(ivec));
467         cksumsz = 16;
468         cksum_func = verify_checksum_md4;
469         break;
470     case ETYPE_DES_CBC_MD5:
471         memset(&ivec, 0, sizeof(ivec));
472         cksumsz = 16;
473         cksum_func = verify_checksum_md5;
474         break;
475     default:
476         abort();
477     }
478
479     des_cbc_encrypt(in, out, insz, s, &ivec, 0);
480
481     memcpy(cksum, (char *)out + CONFOUNDERSZ, cksumsz);
482     memset((char *)out + CONFOUNDERSZ, 0, cksumsz);
483
484     if (cksum_func)
485         ret = (*cksum_func) (out, insz, cksum, cksumsz, key);
486
487     *outsz = insz - CONFOUNDERSZ - cksumsz;
488     memmove(out, (char *)out + CONFOUNDERSZ + cksumsz, *outsz);
489
490     return ret;
491 }