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
_krb5_krb_time_to_life(time_t start,time_t end)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
_krb5_krb_life_to_time(int start,int life_)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
get_krb4_cc_name(const char * tkfile,char ** cc)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
write_v4_cc(krb5_context context,const char * tkfile,krb5_storage * sp,int append)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
_krb5_krb_tf_setup(krb5_context context,struct credentials * v4creds,const char * tkfile,int append)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
_krb5_krb_dest_tkt(krb5_context context,const char * tkfile)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
decrypt_etext(krb5_context context,const krb5_keyblock * key,const krb5_data * cdata,krb5_data * data)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
storage_to_etext(krb5_context context,krb5_storage * sp,const krb5_keyblock * key,krb5_data * enc_data)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
put_nir(krb5_storage * sp,const char * name,const char * instance,const char * realm)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
_krb5_krb_create_ticket(krb5_context context,unsigned char flags,const char * pname,const char * pinstance,const char * prealm,int32_t paddress,const krb5_keyblock * session,int16_t life,int32_t life_sec,const char * sname,const char * sinstance,const krb5_keyblock * key,krb5_data * enc_data)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
_krb5_krb_create_ciph(krb5_context context,const krb5_keyblock * session,const char * service,const char * instance,const char * realm,uint32_t life,unsigned char kvno,const krb5_data * ticket,uint32_t kdc_time,const krb5_keyblock * key,krb5_data * enc_data)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
_krb5_krb_create_auth_reply(krb5_context context,const char * pname,const char * pinst,const char * prealm,int32_t time_ws,int n,uint32_t x_date,unsigned char kvno,const krb5_data * cipher,krb5_data * data)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
_krb5_krb_cr_err_reply(krb5_context context,const char * name,const char * inst,const char * realm,uint32_t time_ws,uint32_t e,const char * e_string,krb5_data * data)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
get_v4_stringz(krb5_storage * sp,char ** str,size_t max_len)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
_krb5_krb_decomp_ticket(krb5_context context,const krb5_data * enc_ticket,const krb5_keyblock * key,const char * local_realm,char ** sname,char ** sinstance,struct _krb5_krb_auth_data * ad)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
_krb5_krb_rd_req(krb5_context context,krb5_data * authent,const char * service,const char * instance,const char * local_realm,int32_t from_addr,const krb5_keyblock * key,struct _krb5_krb_auth_data * ad)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
_krb5_krb_free_auth_data(krb5_context context,struct _krb5_krb_auth_data * ad)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