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