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,v 1.5 2006/05/05 09:31:00 lha Exp $");
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 EINVAL;
355     size = 8 - (size & 7);
356 
357     ret = krb5_storage_write(sp, eightzeros, size);
358     if (ret != size)
359 	return EINVAL;
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 = EINVAL;
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 = EINVAL;
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 = EINVAL;
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 = EINVAL;
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     RCHECK(ret, krb5_store_int32(sp, e), error);
603     RCHECK(ret, krb5_store_stringz(sp, e_string), error);
604 
605     ret = krb5_storage_to_data(sp, data);
606 
607  error:
608     krb5_storage_free(sp);
609     if (ret)
610 	krb5_set_error_string(context, "Failed to encode kerberos 4 error");
611 
612     return 0;
613 }
614 
615 static krb5_error_code
get_v4_stringz(krb5_storage * sp,char ** str,size_t max_len)616 get_v4_stringz(krb5_storage *sp, char **str, size_t max_len)
617 {
618     krb5_error_code ret;
619 
620     ret = krb5_ret_stringz(sp, str);
621     if (ret)
622 	return ret;
623     if (strlen(*str) > max_len) {
624 	free(*str);
625 	*str = NULL;
626 	return EINVAL;
627     }
628     return 0;
629 }
630 
631 /*
632  *
633  */
634 
635 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)636 _krb5_krb_decomp_ticket(krb5_context context,
637 			const krb5_data *enc_ticket,
638 			const krb5_keyblock *key,
639 			const char *local_realm,
640 			char **sname,
641 			char **sinstance,
642 			struct _krb5_krb_auth_data *ad)
643 {
644     krb5_error_code ret;
645     krb5_ssize_t size;
646     krb5_storage *sp = NULL;
647     krb5_data ticket;
648     unsigned char des_key[8];
649 
650     memset(ad, 0, sizeof(*ad));
651     krb5_data_zero(&ticket);
652 
653     *sname = NULL;
654     *sinstance = NULL;
655 
656     RCHECK(ret, decrypt_etext(context, key, enc_ticket, &ticket), error);
657 
658     sp = krb5_storage_from_data(&ticket);
659     if (sp == NULL) {
660 	krb5_data_free(&ticket);
661 	krb5_set_error_string(context, "alloc: out of memory");
662 	return ENOMEM;
663     }
664 
665     krb5_storage_set_eof_code(sp, EINVAL); /* XXX */
666 
667     RCHECK(ret, krb5_ret_int8(sp, &ad->k_flags), error);
668     RCHECK(ret, get_v4_stringz(sp, &ad->pname, ANAME_SZ), error);
669     RCHECK(ret, get_v4_stringz(sp, &ad->pinst, INST_SZ), error);
670     RCHECK(ret, get_v4_stringz(sp, &ad->prealm, REALM_SZ), error);
671     RCHECK(ret, krb5_ret_uint32(sp, &ad->address), error);
672 
673     size = krb5_storage_read(sp, des_key, sizeof(des_key));
674     if (size != sizeof(des_key)) {
675 	ret = EINVAL; /* XXX */
676 	goto error;
677     }
678 
679     RCHECK(ret, krb5_ret_uint8(sp, &ad->life), error);
680 
681     if (ad->k_flags & 1)
682 	krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
683     else
684 	krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
685 
686     RCHECK(ret, krb5_ret_uint32(sp, &ad->time_sec), error);
687 
688     RCHECK(ret, get_v4_stringz(sp, sname, ANAME_SZ), error);
689     RCHECK(ret, get_v4_stringz(sp, sinstance, INST_SZ), error);
690 
691     ret = krb5_keyblock_init(context, ETYPE_DES_PCBC_NONE,
692 			     des_key, sizeof(des_key), &ad->session);
693     if (ret)
694 	goto error;
695 
696     if (strlen(ad->prealm) == 0) {
697 	free(ad->prealm);
698 	ad->prealm = strdup(local_realm);
699 	if (ad->prealm == NULL) {
700 	    ret = ENOMEM;
701 	    goto error;
702 	}
703     }
704 
705  error:
706     memset(des_key, 0, sizeof(des_key));
707     if (sp)
708 	krb5_storage_free(sp);
709     krb5_data_free(&ticket);
710     if (ret) {
711 	if (*sname) {
712 	    free(*sname);
713 	    *sname = NULL;
714 	}
715 	if (*sinstance) {
716 	    free(*sinstance);
717 	    *sinstance = NULL;
718 	}
719 	_krb5_krb_free_auth_data(context, ad);
720 	krb5_set_error_string(context, "Failed to decode v4 ticket");
721     }
722     return ret;
723 }
724 
725 /*
726  *
727  */
728 
729 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)730 _krb5_krb_rd_req(krb5_context context,
731 		 krb5_data *authent,
732 		 const char *service,
733 		 const char *instance,
734 		 const char *local_realm,
735 		 int32_t from_addr,
736 		 const krb5_keyblock *key,
737 		 struct _krb5_krb_auth_data *ad)
738 {
739     krb5_error_code ret;
740     krb5_storage *sp;
741     krb5_data ticket, eaut, aut;
742     krb5_ssize_t size;
743     int little_endian;
744     int8_t pvno;
745     int8_t type;
746     int8_t s_kvno;
747     uint8_t ticket_length;
748     uint8_t eaut_length;
749     uint8_t time_5ms;
750     char *realm = NULL;
751     char *sname = NULL;
752     char *sinstance = NULL;
753     char *r_realm = NULL;
754     char *r_name = NULL;
755     char *r_instance = NULL;
756 
757     uint32_t r_time_sec;	/* Coarse time from authenticator */
758     unsigned long delta_t;      /* Time in authenticator - local time */
759     long tkt_age;		/* Age of ticket */
760 
761     struct timeval tv;
762 
763     krb5_data_zero(&ticket);
764     krb5_data_zero(&eaut);
765     krb5_data_zero(&aut);
766 
767     sp = krb5_storage_from_data(authent);
768     if (sp == NULL) {
769 	krb5_set_error_string(context, "alloc: out of memory");
770 	return ENOMEM;
771     }
772 
773     krb5_storage_set_eof_code(sp, EINVAL); /* XXX */
774 
775     ret = krb5_ret_int8(sp, &pvno);
776     if (ret)
777 	goto error;
778 
779     if (pvno != KRB_PROT_VERSION) {
780 	ret = EINVAL; /* XXX */
781 	goto error;
782     }
783 
784     ret = krb5_ret_int8(sp, &type);
785     if (ret)
786 	goto error;
787 
788     little_endian = type & 1;
789     type &= ~1;
790 
791     if(type != AUTH_MSG_APPL_REQUEST && type != AUTH_MSG_APPL_REQUEST_MUTUAL) {
792 	ret = EINVAL; /* RD_AP_MSG_TYPE */
793 	goto error;
794     }
795 
796     RCHECK(ret, krb5_ret_int8(sp, &s_kvno), error);
797     RCHECK(ret, get_v4_stringz(sp, &realm, REALM_SZ), error);
798     RCHECK(ret, krb5_ret_uint8(sp, &ticket_length), error);
799     RCHECK(ret, krb5_ret_uint8(sp, &eaut_length), error);
800     RCHECK(ret, krb5_data_alloc(&ticket, ticket_length), error);
801 
802     size = krb5_storage_read(sp, ticket.data, ticket.length);
803     if (size != ticket.length) {
804 	ret = EINVAL;
805 	goto error;
806     }
807 
808     /* Decrypt and take apart ticket */
809     ret = _krb5_krb_decomp_ticket(context, &ticket, key, local_realm,
810 				  &sname, &sinstance, ad);
811     if (ret)
812 	goto error;
813 
814     RCHECK(ret, krb5_data_alloc(&eaut, eaut_length), error);
815 
816     size = krb5_storage_read(sp, eaut.data, eaut.length);
817     if (size != eaut.length) {
818 	ret = EINVAL;
819 	goto error;
820     }
821 
822     krb5_storage_free(sp);
823     sp = NULL;
824 
825     ret = decrypt_etext(context, &ad->session, &eaut, &aut);
826     if (ret)
827 	goto error;
828 
829     sp = krb5_storage_from_data(&aut);
830     if (sp == NULL) {
831 	krb5_set_error_string(context, "alloc: out of memory");
832 	ret = ENOMEM;
833 	goto error;
834     }
835 
836     if (little_endian)
837 	krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
838     else
839 	krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
840 
841     RCHECK(ret, get_v4_stringz(sp, &r_name, ANAME_SZ), error);
842     RCHECK(ret, get_v4_stringz(sp, &r_instance, INST_SZ), error);
843     RCHECK(ret, get_v4_stringz(sp, &r_realm, REALM_SZ), error);
844 
845     RCHECK(ret, krb5_ret_uint32(sp, &ad->checksum), error);
846     RCHECK(ret, krb5_ret_uint8(sp, &time_5ms), error);
847     RCHECK(ret, krb5_ret_uint32(sp, &r_time_sec), error);
848 
849     if (strcmp(ad->pname, r_name) != 0 ||
850 	strcmp(ad->pinst, r_instance) != 0 ||
851 	strcmp(ad->prealm, r_realm) != 0) {
852 	ret = EINVAL; /* RD_AP_INCON */
853 	goto error;
854     }
855 
856     if (from_addr && from_addr != ad->address) {
857 	ret = EINVAL; /* RD_AP_BADD */
858 	goto error;
859     }
860 
861     gettimeofday(&tv, NULL);
862     delta_t = abs((int)(tv.tv_sec - r_time_sec));
863     if (delta_t > CLOCK_SKEW) {
864         ret = EINVAL; /* RD_AP_TIME */
865 	goto error;
866     }
867 
868     /* Now check for expiration of ticket */
869 
870     tkt_age = tv.tv_sec - ad->time_sec;
871 
872     if ((tkt_age < 0) && (-tkt_age > CLOCK_SKEW)) {
873         ret = EINVAL; /* RD_AP_NYV */
874 	goto error;
875     }
876 
877     if (tv.tv_sec > _krb5_krb_life_to_time(ad->time_sec, ad->life)) {
878 	ret = EINVAL; /* RD_AP_EXP */
879 	goto error;
880     }
881 
882     ret = 0;
883  error:
884     krb5_data_free(&ticket);
885     krb5_data_free(&eaut);
886     krb5_data_free(&aut);
887     if (realm)
888 	free(realm);
889     if (sname)
890 	free(sname);
891     if (sinstance)
892 	free(sinstance);
893     if (r_name)
894 	free(r_name);
895     if (r_instance)
896 	free(r_instance);
897     if (r_realm)
898 	free(r_realm);
899     if (sp)
900 	krb5_storage_free(sp);
901 
902     if (ret)
903 	krb5_clear_error_string(context);
904 
905     return ret;
906 }
907 
908 /*
909  *
910  */
911 
912 void KRB5_LIB_FUNCTION
_krb5_krb_free_auth_data(krb5_context context,struct _krb5_krb_auth_data * ad)913 _krb5_krb_free_auth_data(krb5_context context, struct _krb5_krb_auth_data *ad)
914 {
915     if (ad->pname)
916 	free(ad->pname);
917     if (ad->pinst)
918 	free(ad->pinst);
919     if (ad->prealm)
920 	free(ad->prealm);
921     krb5_free_keyblock_contents(context, &ad->session);
922     memset(ad, 0, sizeof(*ad));
923 }
924