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"
35RCSID("$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
49static 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
60int 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
84time_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
108static krb5_error_code
109get_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
138static krb5_error_code
139write_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
230krb5_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
286krb5_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
315static krb5_error_code
316decrypt_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
337static const char eightzeros[8] = "\x00\x00\x00\x00\x00\x00\x00\x00";
338
339static krb5_error_code
340storage_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
383static krb5_error_code
384put_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
402krb5_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
460krb5_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
519krb5_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
571krb5_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
618static krb5_error_code
619get_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
638krb5_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
732krb5_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
928void 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}
940