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