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