rxkad-v5-dot-check-20080122
[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 #include <string.h>
85 #include <rx/xdr.h>
86 #include <rx/rx.h>
87 #include <des.h>
88 #include "lifetimes.h"
89 #include "rxkad.h"
90 #endif /* defined(UKERNEL) */
91
92 #include "v5gen-rewrite.h"
93 #include "asn1-common.h"
94 #include "der.h"
95 #include "v5gen.h"
96 #include "v5der.c"
97 #include "v5gen.c"
98 #include "md4.h"
99 #include "md5.h"
100
101 /*
102  * Principal conversion Taken from src/lib/krb5/krb/conv_princ from MIT Kerberos.  If you
103  * find a need to change the services here, please consider opening a
104  * bug with MIT by sending mail to krb5-bugs@mit.edu.
105  */
106
107 struct krb_convert {
108     char *v4_str;
109     char *v5_str;
110     unsigned int flags;
111     unsigned int len;
112 };
113
114 #define DO_REALM_CONVERSION 0x00000001
115
116 /*
117  * Kadmin doesn't do realm conversion because it's currently
118  * kadmin/REALM.NAME.  Zephyr doesn't because it's just zephyr/zephyr.
119  *
120  * "Realm conversion" is a bit of a misnomer; really, the v5 name is
121  * using a FQDN or something that looks like it, where the v4 name is
122  * just using the first label.  Sometimes that second principal name
123  * component is a hostname, sometimes the realm name, sometimes it's
124  * neither.
125  *
126  * This list should probably be more configurable, and more than
127  * likely on a per-realm basis, so locally-defined services can be
128  * added, or not.
129  */
130 static const struct krb_convert sconv_list[] = {
131     /* Realm conversion, Change service name */
132 #define RC(V5NAME,V4NAME) { V5NAME, V4NAME, DO_REALM_CONVERSION, sizeof(V5NAME)-1 }
133     /* Realm conversion */
134 #define R(NAME)         { NAME, NAME, DO_REALM_CONVERSION, sizeof(NAME)-1 }
135     /* No Realm conversion */
136 #define NR(NAME)        { NAME, NAME, 0, sizeof(NAME)-1 }
137
138     NR("kadmin"),
139     RC("rcmd", "host"),
140     R("discuss"),
141     R("rvdsrv"),
142     R("sample"),
143     R("olc"),
144     R("pop"),
145     R("sis"),
146     R("rfs"),
147     R("imap"),
148     R("ftp"),
149     R("ecat"),
150     R("daemon"),
151     R("gnats"),
152     R("moira"),
153     R("prms"),
154     R("mandarin"),
155     R("register"),
156     R("changepw"),
157     R("sms"),
158     R("afpserver"),
159     R("gdss"),
160     R("news"),
161     R("abs"),
162     R("nfs"),
163     R("tftp"),
164     NR("zephyr"),
165     R("http"),
166     R("khttp"),
167     R("pgpsigner"),
168     R("irc"),
169     R("mandarin-agent"),
170     R("write"),
171     R("palladium"),
172     R("imap"),
173     R("smtp"),
174     R("lmtp"),
175     R("ldap"),
176     R("acap"),
177     R("argus"),
178     R("mupdate"),
179     R("argus"),
180     {0, 0, 0, 0},
181 #undef R
182 #undef RC
183 #undef NR
184 };
185
186 static int
187   krb5_des_decrypt(struct ktc_encryptionKey *, int, void *, size_t, void *,
188                    size_t *);
189
190
191
192
193 int
194 tkt_DecodeTicket5(char *ticket, afs_int32 ticket_len,
195                   int (*get_key) (char *, int, struct ktc_encryptionKey *),
196                   char *get_key_rock, int serv_kvno, char *name, char *inst,
197                   char *cell, char *session_key, afs_int32 * host,
198                   afs_int32 * start, afs_int32 * end, afs_int32 disableCheckdot)
199 {
200     char plain[MAXKRB5TICKETLEN];
201     struct ktc_encryptionKey serv_key;
202     Ticket t5;                  /* Must free */
203     EncTicketPart decr_part;    /* Must free */
204     int code;
205     size_t siz, plainsiz;
206     int v5_serv_kvno;
207     char *v5_comp0, *v5_comp1, *c;
208     const struct krb_convert *p;
209
210     memset(&t5, 0, sizeof(t5));
211     memset(&decr_part, 0, sizeof(decr_part));
212
213     *host = 0;
214
215     if (ticket_len == 0)
216         return RXKADBADTICKET;  /* no ticket */
217
218     if (serv_kvno == RXKAD_TKT_TYPE_KERBEROS_V5) {
219         code = decode_Ticket(ticket, ticket_len, &t5, &siz);
220         if (code != 0)
221             goto cleanup;
222
223         if (t5.tkt_vno != 5)
224             goto bad_ticket;
225     } else {
226         code = decode_EncryptedData(ticket, ticket_len, &t5.enc_part, &siz);
227         if (code != 0)
228             goto cleanup;
229     }
230
231     /* If kvno is null, it's probably not included because it was kvno==0 
232      * in the ticket */
233     if (t5.enc_part.kvno == NULL) {
234         v5_serv_kvno = 0;
235     } else {
236         v5_serv_kvno = *t5.enc_part.kvno;
237     }
238
239     /* Check that the key type really fit into 8 bytes */
240     switch (t5.enc_part.etype) {
241     case ETYPE_DES_CBC_CRC:
242     case ETYPE_DES_CBC_MD4:
243     case ETYPE_DES_CBC_MD5:
244         break;
245     default:
246         goto unknown_key;
247     }
248
249     /* check ticket */
250     if (t5.enc_part.cipher.length > sizeof(plain)
251         || t5.enc_part.cipher.length % 8 != 0)
252         goto bad_ticket;
253
254     code = (*get_key) (get_key_rock, v5_serv_kvno, &serv_key);
255     if (code)
256         goto unknown_key;
257
258     /* Decrypt data here, save in plain, assume it will shrink */
259     code =
260         krb5_des_decrypt(&serv_key, t5.enc_part.etype,
261                          t5.enc_part.cipher.data, t5.enc_part.cipher.length,
262                          plain, &plainsiz);
263     if (code != 0)
264         goto bad_ticket;
265
266     /* Decode ticket */
267     code = decode_EncTicketPart(plain, plainsiz, &decr_part, &siz);
268     if (code != 0)
269         goto bad_ticket;
270
271     /* Extract realm and principal */
272     strncpy(cell, decr_part.crealm, MAXKTCNAMELEN);
273     cell[MAXKTCNAMELEN - 1] = '\0';
274     inst[0] = '\0';
275     switch (decr_part.cname.name_string.len) {
276     case 2:
277         v5_comp0 = decr_part.cname.name_string.val[0];
278         v5_comp1 = decr_part.cname.name_string.val[1];
279         p = sconv_list;
280         while (p->v4_str) {
281             if (strcmp(p->v5_str, v5_comp0) == 0) {
282                 /*
283                  * It is, so set the new name now, and chop off
284                  * instance's domain name if requested.
285                  */
286                 strncpy(name, p->v4_str, MAXKTCNAMELEN);
287                 name[MAXKTCNAMELEN - 1] = '\0';
288                 if (p->flags & DO_REALM_CONVERSION) {
289                     c = strchr(v5_comp1, '.');
290                     if (!c || (c - v5_comp1) >= MAXKTCNAMELEN - 1)
291                         goto bad_ticket;
292                     strncpy(inst, v5_comp1, c - v5_comp1);
293                     inst[c - v5_comp1] = '\0';
294                 }
295                 break;
296             }
297             p++;
298         }
299
300         if (!p->v4_str) {
301             strncpy(inst, decr_part.cname.name_string.val[1], MAXKTCNAMELEN);
302             inst[MAXKTCNAMELEN - 1] = '\0';
303             strncpy(name, decr_part.cname.name_string.val[0], MAXKTCNAMELEN);
304             name[MAXKTCNAMELEN - 1] = '\0';
305         }
306         break;
307     case 1:
308         strncpy(name, decr_part.cname.name_string.val[0], MAXKTCNAMELEN);
309         name[MAXKTCNAMELEN - 1] = '\0';
310         break;
311     default:
312         goto bad_ticket;
313     }
314
315     if (!disableCheckdot) {
316         /* 
317          * If the first part of the name_string contains a dot, punt since
318          * then we can't see the diffrence between the kerberos 5
319          * principals foo.root and foo/root later in the fileserver.
320          */
321         if (strchr(decr_part.cname.name_string.val[0], '.') != NULL)
322             goto bad_ticket;
323     }
324
325     /* Verify that decr_part.key is of right type */
326     switch (decr_part.key.keytype) {
327     case ETYPE_DES_CBC_CRC:
328     case ETYPE_DES_CBC_MD4:
329     case ETYPE_DES_CBC_MD5:
330         break;
331     default:
332         goto bad_ticket;
333     }
334
335     if (decr_part.key.keyvalue.length != 8)
336         goto bad_ticket;
337
338     /* Extract session key */
339     memcpy(session_key, decr_part.key.keyvalue.data, 8);
340
341     /* Check lifetimes and host addresses, flags etc */
342     {
343         time_t now = time(0);   /* Use fast time package instead??? */
344         *start = decr_part.authtime;
345         if (decr_part.starttime)
346             *start = *decr_part.starttime;
347 #if 0
348         if (*start - now > CLOCK_SKEW || decr_part.flags.invalid)
349             goto no_auth;
350 #else
351         if (decr_part.flags.invalid)
352             goto no_auth;
353 #endif
354         if (now > decr_part.endtime)
355             goto tkt_expired;
356         *end = decr_part.endtime;
357     }
358
359   cleanup:
360     if (serv_kvno == RXKAD_TKT_TYPE_KERBEROS_V5)
361         free_Ticket(&t5);
362     else
363         free_EncryptedData(&t5.enc_part);
364     free_EncTicketPart(&decr_part);
365     memset(&serv_key, 0, sizeof(serv_key));
366     return code;
367
368   unknown_key:
369     code = RXKADUNKNOWNKEY;
370     goto cleanup;
371   no_auth:
372     code = RXKADNOAUTH;
373     goto cleanup;
374   tkt_expired:
375     code = RXKADEXPIRED;
376     goto cleanup;
377   bad_ticket:
378     code = RXKADBADTICKET;
379     goto cleanup;
380
381 }
382
383 static int
384 verify_checksum_md4(void *data, size_t len,
385                     void *cksum, size_t cksumsz,
386                     struct ktc_encryptionKey *key)
387 {
388     MD4_CTX md4;
389     unsigned char tmp[16];
390
391     MD4_Init(&md4);
392     MD4_Update(&md4, data, len);
393     MD4_Final(tmp, &md4);
394
395     if (memcmp(tmp, cksum, cksumsz) != 0)
396         return 1;
397     return 0;
398 }
399
400 static int
401 verify_checksum_md5(void *data, size_t len,
402                     void *cksum, size_t cksumsz,
403                     struct ktc_encryptionKey *key)
404 {
405     MD5_CTX md5;
406     unsigned char tmp[16];
407
408     MD5_Init(&md5);
409     MD5_Update(&md5, data, len);
410     MD5_Final(tmp, &md5);
411
412     if (memcmp(tmp, cksum, cksumsz) != 0)
413         return 1;
414     return 0;
415 }
416
417 static int
418 verify_checksum_crc(void *data, size_t len, void *cksum, size_t cksumsz,
419                     struct ktc_encryptionKey *key)
420 {
421     afs_uint32 crc;
422     char r[4];
423
424     _rxkad_crc_init_table();
425     crc = _rxkad_crc_update(data, len, 0);
426     r[0] = crc & 0xff;
427     r[1] = (crc >> 8) & 0xff;
428     r[2] = (crc >> 16) & 0xff;
429     r[3] = (crc >> 24) & 0xff;
430
431     if (memcmp(cksum, r, 4) != 0)
432         return 1;
433     return 0;
434 }
435
436
437 static int
438 krb5_des_decrypt(struct ktc_encryptionKey *key, int etype, void *in,
439                  size_t insz, void *out, size_t * outsz)
440 {
441     int (*cksum_func) (void *, size_t, void *, size_t,
442                        struct ktc_encryptionKey *);
443     des_cblock ivec;
444     des_key_schedule s;
445     char cksum[24];
446     size_t cksumsz;
447     int ret = 1;                /* failure */
448
449     cksum_func = NULL;
450
451     des_key_sched(key, &s);
452
453 #define CONFOUNDERSZ 8
454
455     switch (etype) {
456     case ETYPE_DES_CBC_CRC:
457         memcpy(&ivec, key, sizeof(ivec));
458         cksumsz = 4;
459         cksum_func = verify_checksum_crc;
460         break;
461     case ETYPE_DES_CBC_MD4:
462         memset(&ivec, 0, sizeof(ivec));
463         cksumsz = 16;
464         cksum_func = verify_checksum_md4;
465         break;
466     case ETYPE_DES_CBC_MD5:
467         memset(&ivec, 0, sizeof(ivec));
468         cksumsz = 16;
469         cksum_func = verify_checksum_md5;
470         break;
471     default:
472         abort();
473     }
474
475     des_cbc_encrypt(in, out, insz, s, &ivec, 0);
476
477     memcpy(cksum, (char *)out + CONFOUNDERSZ, cksumsz);
478     memset((char *)out + CONFOUNDERSZ, 0, cksumsz);
479
480     if (cksum_func)
481         ret = (*cksum_func) (out, insz, cksum, cksumsz, key);
482
483     *outsz = insz - CONFOUNDERSZ - cksumsz;
484     memmove(out, (char *)out + CONFOUNDERSZ + cksumsz, *outsz);
485
486     return ret;
487 }