]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - crypto/heimdal/lib/krb5/v4_glue.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / crypto / heimdal / lib / krb5 / v4_glue.c
1 /*
2  * Copyright (c) 1997 - 2005 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 #include "krb5_locl.h"
35 RCSID("$Id: v4_glue.c 22071 2007-11-14 20:04:50Z lha $");
36
37 #include "krb5-v4compat.h"
38
39 /*
40  *
41  */
42
43 #define RCHECK(r,func,label) \
44         do { (r) = func ; if (r) goto label; } while(0);
45
46
47 /* include this here, to avoid dependencies on libkrb */
48
49 static const int _tkt_lifetimes[TKTLIFENUMFIXED] = {
50    38400,   41055,   43894,   46929,   50174,   53643,   57352,   61318,
51    65558,   70091,   74937,   80119,   85658,   91581,   97914,  104684,
52   111922,  119661,  127935,  136781,  146239,  156350,  167161,  178720,
53   191077,  204289,  218415,  233517,  249664,  266926,  285383,  305116,
54   326213,  348769,  372885,  398668,  426234,  455705,  487215,  520904,
55   556921,  595430,  636601,  680618,  727680,  777995,  831789,  889303,
56   950794, 1016537, 1086825, 1161973, 1242318, 1328218, 1420057, 1518247,
57  1623226, 1735464, 1855462, 1983758, 2120925, 2267576, 2424367, 2592000
58 };
59
60 int KRB5_LIB_FUNCTION
61 _krb5_krb_time_to_life(time_t start, time_t end)
62 {
63     int i;
64     time_t life = end - start;
65
66     if (life > MAXTKTLIFETIME || life <= 0) 
67         return 0;
68 #if 0    
69     if (krb_no_long_lifetimes) 
70         return (life + 5*60 - 1)/(5*60);
71 #endif
72     
73     if (end >= NEVERDATE)
74         return TKTLIFENOEXPIRE;
75     if (life < _tkt_lifetimes[0]) 
76         return (life + 5*60 - 1)/(5*60);
77     for (i=0; i<TKTLIFENUMFIXED; i++)
78         if (life <= _tkt_lifetimes[i])
79             return i + TKTLIFEMINFIXED;
80     return 0;
81     
82 }
83
84 time_t KRB5_LIB_FUNCTION
85 _krb5_krb_life_to_time(int start, int life_)
86 {
87     unsigned char life = (unsigned char) life_;
88
89 #if 0    
90     if (krb_no_long_lifetimes)
91         return start + life*5*60;
92 #endif
93
94     if (life == TKTLIFENOEXPIRE)
95         return NEVERDATE;
96     if (life < TKTLIFEMINFIXED)
97         return start + life*5*60;
98     if (life > TKTLIFEMAXFIXED)
99         return start + MAXTKTLIFETIME;
100     return start + _tkt_lifetimes[life - TKTLIFEMINFIXED];
101 }
102
103 /*
104  * Get the name of the krb4 credentials cache, will use `tkfile' as
105  * the name if that is passed in. `cc' must be free()ed by caller,
106  */
107
108 static krb5_error_code
109 get_krb4_cc_name(const char *tkfile, char **cc)
110 {
111
112     *cc = NULL;
113     if(tkfile == NULL) {
114         char *path;
115         if(!issuid()) {
116             path = getenv("KRBTKFILE");
117             if (path)
118                 *cc = strdup(path);
119         }
120         if(*cc == NULL)
121             if (asprintf(cc, "%s%u", TKT_ROOT, (unsigned)getuid()) < 0)
122                 return errno;
123     } else {
124         *cc = strdup(tkfile);
125         if (*cc == NULL)
126             return ENOMEM;
127     }
128     return 0;
129 }
130
131 /*
132  * Write a Kerberos 4 ticket file
133  */
134
135 #define KRB5_TF_LCK_RETRY_COUNT 50
136 #define KRB5_TF_LCK_RETRY 1
137
138 static krb5_error_code
139 write_v4_cc(krb5_context context, const char *tkfile, 
140             krb5_storage *sp, int append)
141 {
142     krb5_error_code ret;
143     struct stat sb;
144     krb5_data data;
145     char *path;
146     int fd, i;
147
148     ret = get_krb4_cc_name(tkfile, &path);
149     if (ret) {
150         krb5_set_error_string(context, 
151                               "krb5_krb_tf_setup: failed getting "
152                               "the krb4 credentials cache name"); 
153         return ret;
154     }
155
156     fd = open(path, O_WRONLY|O_CREAT, 0600);
157     if (fd < 0) {
158         ret = errno;
159         krb5_set_error_string(context, 
160                               "krb5_krb_tf_setup: error opening file %s", 
161                               path);
162         free(path);
163         return ret;
164     }
165
166     if (fstat(fd, &sb) != 0 || !S_ISREG(sb.st_mode)) {
167         krb5_set_error_string(context, 
168                               "krb5_krb_tf_setup: tktfile %s is not a file",
169                               path);
170         free(path);
171         close(fd);
172         return KRB5_FCC_PERM;
173     }
174
175     for (i = 0; i < KRB5_TF_LCK_RETRY_COUNT; i++) {
176         if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
177             sleep(KRB5_TF_LCK_RETRY);
178         } else
179             break;
180     }
181     if (i == KRB5_TF_LCK_RETRY_COUNT) {
182         krb5_set_error_string(context,
183                               "krb5_krb_tf_setup: failed to lock %s",
184                               path);
185         free(path);
186         close(fd);
187         return KRB5_FCC_PERM;
188     }
189
190     if (!append) {
191         ret = ftruncate(fd, 0);
192         if (ret < 0) {
193             flock(fd, LOCK_UN);
194             krb5_set_error_string(context,
195                                   "krb5_krb_tf_setup: failed to truncate %s",
196                                   path);
197             free(path);
198             close(fd);
199             return KRB5_FCC_PERM;
200         }
201     }
202     ret = lseek(fd, 0L, SEEK_END);
203     if (ret < 0) {
204         ret = errno;
205         flock(fd, LOCK_UN);
206         free(path);
207         close(fd);
208         return ret;
209     }
210
211     krb5_storage_to_data(sp, &data);
212
213     ret = write(fd, data.data, data.length);
214     if (ret != data.length)
215         ret = KRB5_CC_IO;
216
217     krb5_free_data_contents(context, &data);
218
219     flock(fd, LOCK_UN);
220     free(path);
221     close(fd);
222
223     return 0;
224 }
225
226 /*
227  *
228  */
229
230 krb5_error_code KRB5_LIB_FUNCTION
231 _krb5_krb_tf_setup(krb5_context context, 
232                    struct credentials *v4creds, 
233                    const char *tkfile,
234                    int append)
235 {
236     krb5_error_code ret;
237     krb5_storage *sp;
238
239     sp = krb5_storage_emem();
240     if (sp == NULL)
241         return ENOMEM;
242
243     krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_HOST);
244     krb5_storage_set_eof_code(sp, KRB5_CC_IO);
245
246     krb5_clear_error_string(context);
247
248     if (!append) {
249         RCHECK(ret, krb5_store_stringz(sp, v4creds->pname), error);
250         RCHECK(ret, krb5_store_stringz(sp, v4creds->pinst), error);
251     }
252
253     /* cred */
254     RCHECK(ret, krb5_store_stringz(sp, v4creds->service), error);
255     RCHECK(ret, krb5_store_stringz(sp, v4creds->instance), error);
256     RCHECK(ret, krb5_store_stringz(sp, v4creds->realm), error);
257     ret = krb5_storage_write(sp, v4creds->session, 8);
258     if (ret != 8) {
259         ret = KRB5_CC_IO;
260         goto error;
261     }
262     RCHECK(ret, krb5_store_int32(sp, v4creds->lifetime), error);
263     RCHECK(ret, krb5_store_int32(sp, v4creds->kvno), error);
264     RCHECK(ret, krb5_store_int32(sp, v4creds->ticket_st.length), error);
265
266     ret = krb5_storage_write(sp, v4creds->ticket_st.dat, 
267                              v4creds->ticket_st.length);
268     if (ret != v4creds->ticket_st.length) {
269         ret = KRB5_CC_IO;
270         goto error;
271     }
272     RCHECK(ret, krb5_store_int32(sp, v4creds->issue_date), error);
273
274     ret = write_v4_cc(context, tkfile, sp, append);
275
276  error:
277     krb5_storage_free(sp);
278
279     return ret;
280 }
281
282 /*
283  *
284  */
285
286 krb5_error_code KRB5_LIB_FUNCTION
287 _krb5_krb_dest_tkt(krb5_context context, const char *tkfile)
288 {
289     krb5_error_code ret;
290     char *path;
291
292     ret = get_krb4_cc_name(tkfile, &path);
293     if (ret) {
294         krb5_set_error_string(context, 
295                               "krb5_krb_tf_setup: failed getting "
296                               "the krb4 credentials cache name"); 
297         return ret;
298     }
299
300     if (unlink(path) < 0) {
301         ret = errno;
302         krb5_set_error_string(context, 
303                               "krb5_krb_dest_tkt failed removing the cache "
304                               "with error %s", strerror(ret));
305     }
306     free(path);
307
308     return ret;
309 }
310
311 /*
312  *
313  */
314
315 static krb5_error_code
316 decrypt_etext(krb5_context context, const krb5_keyblock *key,
317               const krb5_data *cdata, krb5_data *data)
318 {
319     krb5_error_code ret;
320     krb5_crypto crypto;
321
322     ret = krb5_crypto_init(context, key, ETYPE_DES_PCBC_NONE, &crypto);
323     if (ret)
324         return ret;
325
326     ret = krb5_decrypt(context, crypto, 0, cdata->data, cdata->length, data);
327     krb5_crypto_destroy(context, crypto);
328
329     return ret;
330 }
331
332
333 /*
334  *
335  */
336
337 static const char eightzeros[8] = "\x00\x00\x00\x00\x00\x00\x00\x00";
338
339 static krb5_error_code
340 storage_to_etext(krb5_context context,
341                  krb5_storage *sp,
342                  const krb5_keyblock *key, 
343                  krb5_data *enc_data)
344 {
345     krb5_error_code ret;
346     krb5_crypto crypto;
347     krb5_ssize_t size;
348     krb5_data data;
349
350     /* multiple of eight bytes */
351
352     size = krb5_storage_seek(sp, 0, SEEK_END);
353     if (size < 0)
354         return KRB4ET_RD_AP_UNDEC;
355     size = 8 - (size & 7);
356
357     ret = krb5_storage_write(sp, eightzeros, size);
358     if (ret != size)
359         return KRB4ET_RD_AP_UNDEC;
360
361     ret = krb5_storage_to_data(sp, &data);
362     if (ret)
363         return ret;
364
365     ret = krb5_crypto_init(context, key, ETYPE_DES_PCBC_NONE, &crypto);
366     if (ret) {
367         krb5_data_free(&data);
368         return ret;
369     }
370
371     ret = krb5_encrypt(context, crypto, 0, data.data, data.length, enc_data);
372
373     krb5_data_free(&data);
374     krb5_crypto_destroy(context, crypto);
375
376     return ret;
377 }
378
379 /*
380  *
381  */
382
383 static krb5_error_code
384 put_nir(krb5_storage *sp, const char *name,
385         const char *instance, const char *realm)
386 {
387     krb5_error_code ret;
388
389     RCHECK(ret, krb5_store_stringz(sp, name), error);
390     RCHECK(ret, krb5_store_stringz(sp, instance), error);
391     if (realm) {
392         RCHECK(ret, krb5_store_stringz(sp, realm), error);
393     }
394  error:
395     return ret;
396 }
397
398 /*
399  *
400  */
401
402 krb5_error_code KRB5_LIB_FUNCTION
403 _krb5_krb_create_ticket(krb5_context context,
404                         unsigned char flags,
405                         const char *pname,
406                         const char *pinstance,
407                         const char *prealm,
408                         int32_t paddress,
409                         const krb5_keyblock *session,
410                         int16_t life,
411                         int32_t life_sec,
412                         const char *sname,
413                         const char *sinstance,
414                         const krb5_keyblock *key,
415                         krb5_data *enc_data)
416 {
417     krb5_error_code ret;
418     krb5_storage *sp;
419
420     krb5_data_zero(enc_data);
421
422     sp = krb5_storage_emem();
423     if (sp == NULL) {
424         krb5_set_error_string(context, "malloc: out of memory");
425         return ENOMEM;
426     }
427     krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
428
429     RCHECK(ret, krb5_store_int8(sp, flags), error);
430     RCHECK(ret, put_nir(sp, pname, pinstance, prealm), error);
431     RCHECK(ret, krb5_store_int32(sp, ntohl(paddress)), error);
432
433     /* session key */
434     ret = krb5_storage_write(sp,
435                              session->keyvalue.data, 
436                              session->keyvalue.length);
437     if (ret != session->keyvalue.length) {
438         ret = KRB4ET_INTK_PROT;
439         goto error;
440     }
441
442     RCHECK(ret, krb5_store_int8(sp, life), error);
443     RCHECK(ret, krb5_store_int32(sp, life_sec), error);
444     RCHECK(ret, put_nir(sp, sname, sinstance, NULL), error);
445
446     ret = storage_to_etext(context, sp, key, enc_data);
447
448  error:
449     krb5_storage_free(sp);
450     if (ret)
451         krb5_set_error_string(context, "Failed to encode kerberos 4 ticket");
452
453     return ret;
454 }
455
456 /*
457  *
458  */
459
460 krb5_error_code KRB5_LIB_FUNCTION
461 _krb5_krb_create_ciph(krb5_context context,
462                       const krb5_keyblock *session,
463                       const char *service,
464                       const char *instance,
465                       const char *realm,
466                       uint32_t life,
467                       unsigned char kvno,
468                       const krb5_data *ticket,
469                       uint32_t kdc_time,
470                       const krb5_keyblock *key,
471                       krb5_data *enc_data)
472 {
473     krb5_error_code ret;
474     krb5_storage *sp;
475
476     krb5_data_zero(enc_data);
477
478     sp = krb5_storage_emem();
479     if (sp == NULL) {
480         krb5_set_error_string(context, "malloc: out of memory");
481         return ENOMEM;
482     }
483     krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
484
485     /* session key */
486     ret = krb5_storage_write(sp,
487                              session->keyvalue.data, 
488                              session->keyvalue.length);
489     if (ret != session->keyvalue.length) {
490         ret = KRB4ET_INTK_PROT;
491         goto error;
492     }
493
494     RCHECK(ret, put_nir(sp, service, instance, realm), error);
495     RCHECK(ret, krb5_store_int8(sp, life), error);
496     RCHECK(ret, krb5_store_int8(sp, kvno), error);
497     RCHECK(ret, krb5_store_int8(sp, ticket->length), error);
498     ret = krb5_storage_write(sp, ticket->data, ticket->length);
499     if (ret != ticket->length) {
500         ret = KRB4ET_INTK_PROT;
501         goto error;
502     }
503     RCHECK(ret, krb5_store_int32(sp, kdc_time), error);
504
505     ret = storage_to_etext(context, sp, key, enc_data);
506
507  error:
508     krb5_storage_free(sp);
509     if (ret)
510         krb5_set_error_string(context, "Failed to encode kerberos 4 ticket");
511
512     return ret;
513 }
514
515 /*
516  *
517  */
518
519 krb5_error_code KRB5_LIB_FUNCTION
520 _krb5_krb_create_auth_reply(krb5_context context,
521                             const char *pname,
522                             const char *pinst,
523                             const char *prealm,
524                             int32_t time_ws,
525                             int n,
526                             uint32_t x_date,
527                             unsigned char kvno,
528                             const krb5_data *cipher,
529                             krb5_data *data)
530 {
531     krb5_error_code ret;
532     krb5_storage *sp;
533
534     krb5_data_zero(data);
535
536     sp = krb5_storage_emem();
537     if (sp == NULL) {
538         krb5_set_error_string(context, "malloc: out of memory");
539         return ENOMEM;
540     }
541     krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
542
543     RCHECK(ret, krb5_store_int8(sp, KRB_PROT_VERSION), error);
544     RCHECK(ret, krb5_store_int8(sp, AUTH_MSG_KDC_REPLY), error);
545     RCHECK(ret, put_nir(sp, pname, pinst, prealm), error);
546     RCHECK(ret, krb5_store_int32(sp, time_ws), error);
547     RCHECK(ret, krb5_store_int8(sp, n), error);
548     RCHECK(ret, krb5_store_int32(sp, x_date), error);
549     RCHECK(ret, krb5_store_int8(sp, kvno), error);
550     RCHECK(ret, krb5_store_int16(sp, cipher->length), error);
551     ret = krb5_storage_write(sp, cipher->data, cipher->length);
552     if (ret != cipher->length) {
553         ret = KRB4ET_INTK_PROT;
554         goto error;
555     }
556
557     ret = krb5_storage_to_data(sp, data);
558
559  error:
560     krb5_storage_free(sp);
561     if (ret)
562         krb5_set_error_string(context, "Failed to encode kerberos 4 ticket");
563         
564     return ret;
565 }
566
567 /*
568  *
569  */
570
571 krb5_error_code KRB5_LIB_FUNCTION
572 _krb5_krb_cr_err_reply(krb5_context context,
573                        const char *name,
574                        const char *inst,
575                        const char *realm,
576                        uint32_t time_ws,
577                        uint32_t e,
578                        const char *e_string,
579                        krb5_data *data)
580 {
581     krb5_error_code ret;
582     krb5_storage *sp;
583
584     krb5_data_zero(data);
585
586     if (name == NULL) name = "";
587     if (inst == NULL) inst = "";
588     if (realm == NULL) realm = "";
589     if (e_string == NULL) e_string = "";
590
591     sp = krb5_storage_emem();
592     if (sp == NULL) {
593         krb5_set_error_string(context, "malloc: out of memory");
594         return ENOMEM;
595     }
596     krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
597
598     RCHECK(ret, krb5_store_int8(sp, KRB_PROT_VERSION), error);
599     RCHECK(ret, krb5_store_int8(sp, AUTH_MSG_ERR_REPLY), error);
600     RCHECK(ret, put_nir(sp, name, inst, realm), error);
601     RCHECK(ret, krb5_store_int32(sp, time_ws), error);
602     /* If it is a Kerberos 4 error-code, remove the et BASE */
603     if (e >= ERROR_TABLE_BASE_krb && e <= ERROR_TABLE_BASE_krb + 255)
604         e -= ERROR_TABLE_BASE_krb;
605     RCHECK(ret, krb5_store_int32(sp, e), error);
606     RCHECK(ret, krb5_store_stringz(sp, e_string), error);
607
608     ret = krb5_storage_to_data(sp, data);
609
610  error:
611     krb5_storage_free(sp);
612     if (ret)
613         krb5_set_error_string(context, "Failed to encode kerberos 4 error");
614         
615     return 0;
616 }
617
618 static krb5_error_code
619 get_v4_stringz(krb5_storage *sp, char **str, size_t max_len)
620 {
621     krb5_error_code ret;
622
623     ret = krb5_ret_stringz(sp, str);
624     if (ret)
625         return ret;
626     if (strlen(*str) > max_len) {
627         free(*str);
628         *str = NULL;
629         return KRB4ET_INTK_PROT;
630     }
631     return 0;
632 }
633
634 /*
635  *
636  */
637
638 krb5_error_code KRB5_LIB_FUNCTION
639 _krb5_krb_decomp_ticket(krb5_context context,
640                         const krb5_data *enc_ticket,
641                         const krb5_keyblock *key,
642                         const char *local_realm,
643                         char **sname,
644                         char **sinstance,
645                         struct _krb5_krb_auth_data *ad)
646 {
647     krb5_error_code ret;
648     krb5_ssize_t size;
649     krb5_storage *sp = NULL;
650     krb5_data ticket;
651     unsigned char des_key[8];
652
653     memset(ad, 0, sizeof(*ad));
654     krb5_data_zero(&ticket);
655
656     *sname = NULL;
657     *sinstance = NULL;
658
659     RCHECK(ret, decrypt_etext(context, key, enc_ticket, &ticket), error);
660
661     sp = krb5_storage_from_data(&ticket);
662     if (sp == NULL) {
663         krb5_data_free(&ticket);
664         krb5_set_error_string(context, "alloc: out of memory");
665         return ENOMEM;
666     }
667
668     krb5_storage_set_eof_code(sp, KRB4ET_INTK_PROT);
669
670     RCHECK(ret, krb5_ret_int8(sp, &ad->k_flags), error);
671     RCHECK(ret, get_v4_stringz(sp, &ad->pname, ANAME_SZ), error);
672     RCHECK(ret, get_v4_stringz(sp, &ad->pinst, INST_SZ), error);
673     RCHECK(ret, get_v4_stringz(sp, &ad->prealm, REALM_SZ), error);
674     RCHECK(ret, krb5_ret_uint32(sp, &ad->address), error);
675         
676     size = krb5_storage_read(sp, des_key, sizeof(des_key));
677     if (size != sizeof(des_key)) {
678         ret = KRB4ET_INTK_PROT;
679         goto error;
680     }
681
682     RCHECK(ret, krb5_ret_uint8(sp, &ad->life), error);
683
684     if (ad->k_flags & 1)
685         krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
686     else
687         krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
688
689     RCHECK(ret, krb5_ret_uint32(sp, &ad->time_sec), error);
690
691     RCHECK(ret, get_v4_stringz(sp, sname, ANAME_SZ), error);
692     RCHECK(ret, get_v4_stringz(sp, sinstance, INST_SZ), error);
693
694     ret = krb5_keyblock_init(context, ETYPE_DES_PCBC_NONE,
695                              des_key, sizeof(des_key), &ad->session);
696     if (ret)
697         goto error;
698
699     if (strlen(ad->prealm) == 0) {
700         free(ad->prealm);
701         ad->prealm = strdup(local_realm);
702         if (ad->prealm == NULL) {
703             ret = ENOMEM;
704             goto error;
705         }
706     }
707
708  error:
709     memset(des_key, 0, sizeof(des_key));
710     if (sp)
711         krb5_storage_free(sp);
712     krb5_data_free(&ticket);
713     if (ret) {
714         if (*sname) {
715             free(*sname);
716             *sname = NULL;
717         }
718         if (*sinstance) {
719             free(*sinstance);
720             *sinstance = NULL;
721         }
722         _krb5_krb_free_auth_data(context, ad);
723         krb5_set_error_string(context, "Failed to decode v4 ticket");
724     }
725     return ret;
726 }
727
728 /*
729  *
730  */
731
732 krb5_error_code KRB5_LIB_FUNCTION
733 _krb5_krb_rd_req(krb5_context context,
734                  krb5_data *authent,
735                  const char *service,
736                  const char *instance,
737                  const char *local_realm,
738                  int32_t from_addr,
739                  const krb5_keyblock *key,
740                  struct _krb5_krb_auth_data *ad)
741 {
742     krb5_error_code ret;
743     krb5_storage *sp;
744     krb5_data ticket, eaut, aut;
745     krb5_ssize_t size;
746     int little_endian;
747     int8_t pvno;
748     int8_t type;
749     int8_t s_kvno;
750     uint8_t ticket_length;
751     uint8_t eaut_length;
752     uint8_t time_5ms;
753     char *realm = NULL;
754     char *sname = NULL;
755     char *sinstance = NULL;
756     char *r_realm = NULL;
757     char *r_name = NULL;
758     char *r_instance = NULL;
759
760     uint32_t r_time_sec;        /* Coarse time from authenticator */
761     unsigned long delta_t;      /* Time in authenticator - local time */
762     long tkt_age;               /* Age of ticket */
763
764     struct timeval tv;
765
766     krb5_data_zero(&ticket);
767     krb5_data_zero(&eaut);
768     krb5_data_zero(&aut);
769
770     sp = krb5_storage_from_data(authent);
771     if (sp == NULL) {
772         krb5_set_error_string(context, "alloc: out of memory");
773         return ENOMEM;
774     }
775
776     krb5_storage_set_eof_code(sp, KRB4ET_INTK_PROT);
777
778     ret = krb5_ret_int8(sp, &pvno);
779     if (ret) {
780         krb5_set_error_string(context, "Failed reading v4 pvno");
781         goto error;
782     }
783
784     if (pvno != KRB_PROT_VERSION) {
785         ret = KRB4ET_RD_AP_VERSION;
786         krb5_set_error_string(context, "Failed v4 pvno not 4");
787         goto error;
788     }
789
790     ret = krb5_ret_int8(sp, &type);
791     if (ret) {
792         krb5_set_error_string(context, "Failed readin v4 type");
793         goto error;
794     }
795
796     little_endian = type & 1;
797     type &= ~1;
798     
799     if(type != AUTH_MSG_APPL_REQUEST && type != AUTH_MSG_APPL_REQUEST_MUTUAL) {
800         ret = KRB4ET_RD_AP_MSG_TYPE;
801         krb5_set_error_string(context, "Not a valid v4 request type");
802         goto error;
803     }
804
805     RCHECK(ret, krb5_ret_int8(sp, &s_kvno), error);
806     RCHECK(ret, get_v4_stringz(sp, &realm, REALM_SZ), error);
807     RCHECK(ret, krb5_ret_uint8(sp, &ticket_length), error);
808     RCHECK(ret, krb5_ret_uint8(sp, &eaut_length), error);
809     RCHECK(ret, krb5_data_alloc(&ticket, ticket_length), error);
810
811     size = krb5_storage_read(sp, ticket.data, ticket.length);
812     if (size != ticket.length) {
813         ret = KRB4ET_INTK_PROT;
814         krb5_set_error_string(context, "Failed reading v4 ticket");
815         goto error;
816     }
817
818     /* Decrypt and take apart ticket */
819     ret = _krb5_krb_decomp_ticket(context, &ticket, key, local_realm, 
820                                   &sname, &sinstance, ad);
821     if (ret)
822         goto error;
823
824     RCHECK(ret, krb5_data_alloc(&eaut, eaut_length), error);
825
826     size = krb5_storage_read(sp, eaut.data, eaut.length);
827     if (size != eaut.length) {
828         ret = KRB4ET_INTK_PROT;
829         krb5_set_error_string(context, "Failed reading v4 authenticator");
830         goto error;
831     }
832
833     krb5_storage_free(sp);
834     sp = NULL;
835
836     ret = decrypt_etext(context, &ad->session, &eaut, &aut);
837     if (ret)
838         goto error;
839
840     sp = krb5_storage_from_data(&aut);
841     if (sp == NULL) {
842         ret = ENOMEM;
843         krb5_set_error_string(context, "alloc: out of memory");
844         goto error;
845     }
846
847     if (little_endian)
848         krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
849     else
850         krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
851
852     RCHECK(ret, get_v4_stringz(sp, &r_name, ANAME_SZ), error);
853     RCHECK(ret, get_v4_stringz(sp, &r_instance, INST_SZ), error);
854     RCHECK(ret, get_v4_stringz(sp, &r_realm, REALM_SZ), error);
855
856     RCHECK(ret, krb5_ret_uint32(sp, &ad->checksum), error);
857     RCHECK(ret, krb5_ret_uint8(sp, &time_5ms), error);
858     RCHECK(ret, krb5_ret_uint32(sp, &r_time_sec), error);
859
860     if (strcmp(ad->pname, r_name) != 0 ||
861         strcmp(ad->pinst, r_instance) != 0 ||
862         strcmp(ad->prealm, r_realm) != 0) {
863         krb5_set_error_string(context, "v4 principal mismatch");
864         ret = KRB4ET_RD_AP_INCON;
865         goto error;
866     }
867     
868     if (from_addr && ad->address && from_addr != ad->address) {
869         krb5_set_error_string(context, "v4 bad address in ticket");
870         ret = KRB4ET_RD_AP_BADD;
871         goto error;
872     }
873
874     gettimeofday(&tv, NULL);
875     delta_t = abs((int)(tv.tv_sec - r_time_sec));
876     if (delta_t > CLOCK_SKEW) {
877         ret = KRB4ET_RD_AP_TIME;
878         krb5_set_error_string(context, "v4 clock skew");
879         goto error;
880     }
881
882     /* Now check for expiration of ticket */
883
884     tkt_age = tv.tv_sec - ad->time_sec;
885     
886     if ((tkt_age < 0) && (-tkt_age > CLOCK_SKEW)) {
887         ret = KRB4ET_RD_AP_NYV;
888         krb5_set_error_string(context, "v4 clock skew for expiration");
889         goto error;
890     }
891
892     if (tv.tv_sec > _krb5_krb_life_to_time(ad->time_sec, ad->life)) {
893         ret = KRB4ET_RD_AP_EXP;
894         krb5_set_error_string(context, "v4 ticket expired");
895         goto error;
896     }
897
898     ret = 0;
899  error:
900     krb5_data_free(&ticket);
901     krb5_data_free(&eaut);
902     krb5_data_free(&aut);
903     if (realm)
904         free(realm);
905     if (sname)
906         free(sname);
907     if (sinstance)
908         free(sinstance);
909     if (r_name)
910         free(r_name);
911     if (r_instance)
912         free(r_instance);
913     if (r_realm)
914         free(r_realm);
915     if (sp)
916         krb5_storage_free(sp);
917
918     if (ret)
919         krb5_clear_error_string(context);
920
921     return ret;
922 }
923
924 /*
925  *
926  */
927
928 void KRB5_LIB_FUNCTION
929 _krb5_krb_free_auth_data(krb5_context context, struct _krb5_krb_auth_data *ad)
930 {
931     if (ad->pname)
932         free(ad->pname);
933     if (ad->pinst)
934         free(ad->pinst);
935     if (ad->prealm)
936         free(ad->prealm);
937     krb5_free_keyblock_contents(context, &ad->session);
938     memset(ad, 0, sizeof(*ad));
939 }