aboutsummaryrefslogtreecommitdiff
path: root/src/krb524/cnv_tkt_skey.c
blob: f34ea6f4405a18d329b13cf16c1489fd83e714ad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
/*
 * Copyright 1994 by OpenVision Technologies, Inc.
 * 
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appears in all copies and
 * that both that copyright notice and this permission notice appear in
 * supporting documentation, and that the name of OpenVision not be used
 * in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission. OpenVision makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 * 
 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

#include "k5-int.h"		/* we need krb5_context::clockskew */
#include <stdio.h>
#include <sys/types.h>

#ifdef _WIN32
#include "port-sockets.h"
#else
#include <sys/time.h>
#include <netinet/in.h>
#endif
#if TARGET_OS_MAC
#include <Kerberos/krb.h>
#include <Kerberos/krb524.h>
#include "cr_tkt.h"
#else
#include <krb.h>
#include "krb524.h"
#endif

static int
krb524int_krb_create_ticket(KTEXT, unsigned int, char *, char *, char *, long,
			    char *, int, long, char *, char *, C_Block);

static int
krb524int_krb_cr_tkt_krb5(KTEXT, unsigned int, char *, char *, char *, long,
			  char *, int, long, char *, char *,
			  krb5_keyblock *);

static int
krb524int_krb_cr_tkt_int(KTEXT, unsigned int, char *, char *, char *, long,
			 char *, int, long, char *, char *, C_Block,
			 krb5_keyblock *);

/*
 * Convert a v5 ticket for server to a v4 ticket, using service key
 * skey for both.
 */
int krb524_convert_tkt_skey(context, v5tkt, v4tkt, v5_skey, v4_skey,
			    saddr)
     krb5_context context;
     krb5_ticket *v5tkt;
     KTEXT_ST *v4tkt;
     krb5_keyblock *v5_skey, *v4_skey;
     struct sockaddr_in *saddr;
{
     char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ];
     char sname[ANAME_SZ], sinst[INST_SZ], srealm[REALM_SZ];
     krb5_enc_tkt_part *v5etkt;
     int ret, lifetime, v4endtime;
     krb5_timestamp server_time;
     struct sockaddr_in *sinp = (struct sockaddr_in *)saddr;
     krb5_address kaddr;

     v5tkt->enc_part2 = NULL;
     if ((ret = krb5_decrypt_tkt_part(context, v5_skey, v5tkt))) {
	  return ret;
     }
     v5etkt = v5tkt->enc_part2;

     if (v5etkt->transited.tr_contents.length != 0) {
	 /* Some intermediate realms transited -- do we accept them?

	    Simple answer: No.

	    More complicated answer: Check our local config file to
	    see if the path is correct, and base the answer on that.
	    This denies the krb4 application server any ability to do
	    its own validation as krb5 servers can.

	    Fast answer: Not right now.  */
	  krb5_free_enc_tkt_part(context, v5etkt);
	  v5tkt->enc_part2 = NULL;
	  return KRB5KRB_AP_ERR_ILL_CR_TKT;
     }
     /* We could also encounter a case where luser@R1 gets a ticket
	for krbtgt/R3@R2, and then tries to convert it.  But the
	converted ticket would be one the v4 KDC code should reject
	anyways.  So we don't need to worry about it here.  */

     if ((ret = krb524_convert_princs(context, v5etkt->client, v5tkt->server,
				     pname, pinst, prealm, sname,
				     sinst, srealm))) {
	  krb5_free_enc_tkt_part(context, v5etkt);
	  v5tkt->enc_part2 = NULL;
	  return ret;
     }
     if ((v5etkt->session->enctype != ENCTYPE_DES_CBC_CRC &&
	  v5etkt->session->enctype != ENCTYPE_DES_CBC_MD4 &&
	  v5etkt->session->enctype != ENCTYPE_DES_CBC_MD5) ||
	 v5etkt->session->length != sizeof(C_Block)) {
	  if (krb524_debug)
	       fprintf(stderr, "v5 session keyblock type %d length %d != C_Block size %d\n",
		       v5etkt->session->enctype,
		       v5etkt->session->length,
		       (int) sizeof(C_Block));
	  krb5_free_enc_tkt_part(context, v5etkt);
	  v5tkt->enc_part2 = NULL;
	  return KRB524_BADKEY;
     }
     
     /* V4 has no concept of authtime or renew_till, so ignore them */
     if (v5etkt->times.starttime == 0)
	  v5etkt->times.starttime = v5etkt->times.authtime;
     /* rather than apply fit an extended v5 lifetime into a v4 range,
	give out a v4 ticket with as much of the v5 lifetime is available
	"now" instead. */
     if ((ret = krb5_timeofday(context, &server_time))) {
         if (krb524_debug)
	      fprintf(stderr, "krb5_timeofday failed!\n");
	 krb5_free_enc_tkt_part(context, v5etkt);
	 v5tkt->enc_part2 = NULL;
	 return ret;       
     }
     if ((server_time + context->clockskew >= v5etkt->times.starttime)
	 && (server_time - context->clockskew <= v5etkt->times.endtime)) {
	  lifetime = krb_time_to_life(server_time, v5etkt->times.endtime);
	  v4endtime = krb_life_to_time(v5etkt->times.starttime, lifetime);
	  /*
	   * Adjust start time backwards if the lifetime value
	   * returned by krb_time_to_life() maps to a longer lifetime
	   * than that of the original krb5 ticket.
	   */
	  if (v4endtime > v5etkt->times.endtime)
	      server_time -= v4endtime - v5etkt->times.endtime;
     } else {
          if (krb524_debug)
	       fprintf(stderr, "v5 ticket time out of bounds\n");
	  krb5_free_enc_tkt_part(context, v5etkt);
	  v5tkt->enc_part2 = NULL;
	  if (server_time+context->clockskew < v5etkt->times.starttime)
	       return KRB5KRB_AP_ERR_TKT_NYV;
	  else if (server_time-context->clockskew > v5etkt->times.endtime)
	       return KRB5KRB_AP_ERR_TKT_EXPIRED;
	  else /* shouldn't happen, but just in case... */
	    return KRB5KRB_AP_ERR_TKT_NYV;
     }

     kaddr.addrtype = ADDRTYPE_INET;
     kaddr.length = sizeof(sinp->sin_addr);
     kaddr.contents = (krb5_octet *)&sinp->sin_addr;

     if (!krb5_address_search(context, &kaddr, v5etkt->caddrs)) {
	 if (krb524_debug)
	     fprintf(stderr, "Invalid v5creds address information.\n");
	 krb5_free_enc_tkt_part(context, v5etkt);
	 v5tkt->enc_part2 = NULL;
	 return KRB524_BADADDR;
     }

     if (krb524_debug)
	printf("startime = %ld, authtime = %ld, lifetime = %ld\n",
	       (long) v5etkt->times.starttime,
	       (long) v5etkt->times.authtime,
	       (long) lifetime);

     /* XXX are there V5 flags we should map to V4 equivalents? */
     if (v4_skey->enctype == ENCTYPE_DES_CBC_CRC) {
	 ret = krb524int_krb_create_ticket(v4tkt,
				 0, /* flags */			     
				 pname,
				 pinst,
				 prealm,
				 *((unsigned long *)kaddr.contents),
				 (char *) v5etkt->session->contents,
				 lifetime,
				 /* issue_data */
				 server_time,
				 sname,
				 sinst,
				 v4_skey->contents);
     } else {
	 /* Force enctype to be raw if using DES3. */
	 if (v4_skey->enctype == ENCTYPE_DES3_CBC_SHA1 ||
	     v4_skey->enctype == ENCTYPE_LOCAL_DES3_HMAC_SHA1)
	     v4_skey->enctype = ENCTYPE_DES3_CBC_RAW;
	 ret = krb524int_krb_cr_tkt_krb5(v4tkt,
			       0, /* flags */			     
			       pname,
			       pinst,
			       prealm,
			       *((unsigned long *)kaddr.contents),
			       (char *) v5etkt->session->contents,
			       lifetime,
			       /* issue_data */
			       server_time,
			       sname,
			       sinst,
			       v4_skey);
     }

     krb5_free_enc_tkt_part(context, v5etkt);
     v5tkt->enc_part2 = NULL;
     if (ret == KSUCCESS)
	  return 0;
     else
	  return KRB524_V4ERR;
}

/*****************************************************************************
 * Copied from krb4's cr_tkt.
 * Modified functions below to be static.
 *****************************************************************************/

#define          HOST_BYTE_ORDER (* (const char *) &temp_ONE)
static const int temp_ONE = 1;

/*
 * Create ticket takes as arguments information that should be in a
 * ticket, and the KTEXT object in which the ticket should be
 * constructed.  It then constructs a ticket and returns, leaving the
 * newly created ticket in tkt.
#ifndef NOENCRYPTION
 * The data in tkt->dat is encrypted in the server's key.
#endif
 * The length of the ticket is a multiple of
 * eight bytes and is in tkt->length.
 *
 * If the ticket is too long, the ticket will contain nulls.
 * The return value of the routine is undefined.
 *
 * The corresponding routine to extract information from a ticket it
 * decomp_ticket.  When changes are made to this routine, the
 * corresponding changes should also be made to that file.
 *
 * The packet is built in the following format:
 * 
 * 			variable
 * type			or constant	   data
 * ----			-----------	   ----
 *
 * tkt->length		length of ticket (multiple of 8 bytes)
 * 
#ifdef NOENCRYPTION
 * tkt->dat:
#else
 * tkt->dat:		(encrypted in server's key)
#endif
 * 
 * unsigned char	flags		   namely, HOST_BYTE_ORDER
 * 
 * string		pname		   client's name
 * 
 * string		pinstance	   client's instance
 * 
 * string		prealm		   client's realm
 * 
 * 4 bytes		paddress	   client's address
 * 
 * 8 bytes		session		   session key
 * 
 * 1 byte		life		   ticket lifetime
 * 
 * 4 bytes		time_sec	   KDC timestamp
 * 
 * string		sname		   service's name
 * 
 * string		sinstance	   service's instance
 * 
 * <=7 bytes		null		   null pad to 8 byte multiple
 *
 */
static int
krb524int_krb_create_ticket(tkt, flags, pname, pinstance, prealm, paddress,
		  session, life, time_sec, sname, sinstance, key)
    KTEXT   tkt;                /* Gets filled in by the ticket */
    unsigned int flags;		/* Various Kerberos flags */
    char    *pname;             /* Principal's name */
    char    *pinstance;         /* Principal's instance */
    char    *prealm;            /* Principal's authentication domain */
    long    paddress;           /* Net address of requesting entity */
    char    *session;           /* Session key inserted in ticket */
    int     life;               /* Lifetime of the ticket */
    long    time_sec;           /* Issue time and date */
    char    *sname;             /* Service Name */
    char    *sinstance;         /* Instance Name */
    C_Block key;                /* Service's secret key */
{
    return krb524int_krb_cr_tkt_int(tkt, flags, pname, pinstance, prealm,
				    paddress, session, life, time_sec, sname,
				    sinstance, key, NULL);
}

static int
krb524int_krb_cr_tkt_krb5(tkt, flags, pname, pinstance, prealm, paddress,
			  session, life, time_sec, sname, sinstance, k5key)
    KTEXT   tkt;                /* Gets filled in by the ticket */
    unsigned int flags;		/* Various Kerberos flags */
    char    *pname;             /* Principal's name */
    char    *pinstance;         /* Principal's instance */
    char    *prealm;            /* Principal's authentication domain */
    long    paddress;           /* Net address of requesting entity */
    char    *session;           /* Session key inserted in ticket */
    int     life;               /* Lifetime of the ticket */
    long    time_sec;           /* Issue time and date */
    char    *sname;             /* Service Name */
    char    *sinstance;         /* Instance Name */
    krb5_keyblock *k5key;	/* NULL if not present */
{
    C_Block key;

    return krb524int_krb_cr_tkt_int(tkt, flags, pname, pinstance, prealm,
				    paddress, session, life, time_sec, sname,
				    sinstance, key, k5key);
}

static int
krb524int_krb_cr_tkt_int(tkt, flags_in, pname, pinstance, prealm, paddress,
	       session, life, time_sec, sname, sinstance, key, k5key)
    KTEXT   tkt;                /* Gets filled in by the ticket */
    unsigned int flags_in;	/* Various Kerberos flags */
    char    *pname;             /* Principal's name */
    char    *pinstance;         /* Principal's instance */
    char    *prealm;            /* Principal's authentication domain */
    long    paddress;           /* Net address of requesting entity */
    char    *session;           /* Session key inserted in ticket */
    int     life;               /* Lifetime of the ticket */
    long    time_sec;           /* Issue time and date */
    char    *sname;             /* Service Name */
    char    *sinstance;         /* Instance Name */
    C_Block key;                /* Service's secret key */
    krb5_keyblock *k5key;	/* NULL if not present */
{
    Key_schedule key_s;
    register char *data;        /* running index into ticket */

    unsigned char flags = flags_in & 0xFF; /* This must be one byte */

    tkt->length = 0;            /* Clear previous data  */

    /* Check length of ticket */
    if (sizeof(tkt->dat) < (sizeof(flags) +
                            1 + strlen(pname) +
                            1 + strlen(pinstance) +
                            1 + strlen(prealm) +
                            4 +                         /* address */
			    8 +                         /* session */
			    1 +                         /* life */
			    4 +                         /* issue time */
                            1 + strlen(sname) +
                            1 + strlen(sinstance) +
			    7) / 8) {                   /* roundoff */
        memset(tkt->dat, 0, sizeof(tkt->dat));
        return KFAILURE /* XXX */;
    }

    flags |= HOST_BYTE_ORDER;   /* ticket byte order   */
    memcpy((char *) (tkt->dat), (char *) &flags, sizeof(flags));
    data = ((char *)tkt->dat) + sizeof(flags);
    (void) strcpy(data, pname);
    data += 1 + strlen(pname);
    (void) strcpy(data, pinstance);
    data += 1 + strlen(pinstance);
    (void) strcpy(data, prealm);
    data += 1 + strlen(prealm);
    memcpy(data, (char *) &paddress, 4);
    data += 4;

    memcpy(data, (char *) session, 8);
    data += 8;
    *(data++) = (char) life;
    /* issue time */
    memcpy(data, (char *) &time_sec, 4);
    data += 4;
    (void) strcpy(data, sname);
    data += 1 + strlen(sname);
    (void) strcpy(data, sinstance);
    data += 1 + strlen(sinstance);

    /* guarantee null padded ticket to multiple of 8 bytes */
    memset(data, 0, 7);
    tkt->length = ((data - ((char *)tkt->dat) + 7)/8)*8;

    /* Check length of ticket */
    if (tkt->length > (sizeof(KTEXT_ST) - 7)) {
        memset(tkt->dat, 0, tkt->length);
        tkt->length = 0;
        return KFAILURE /* XXX */;
    }

#ifndef NOENCRYPTION
    /* Encrypt the ticket in the services key */
    if (k5key != NULL) {
	/* block locals */
	krb5_data in;
	krb5_enc_data out;
	krb5_error_code ret;
	size_t enclen;

	in.length = tkt->length;
	in.data = tkt->dat;
	/* XXX assumes context arg is ignored */
	ret = krb5_c_encrypt_length(NULL, k5key->enctype,
				    (size_t)in.length, &enclen);
	if (ret)
	    return KFAILURE;
	out.ciphertext.length = enclen;
	out.ciphertext.data = malloc(enclen);
	if (out.ciphertext.data == NULL)
	    return KFAILURE;	/* XXX maybe ENOMEM? */

	/* XXX assumes context arg is ignored */
	ret = krb5_c_encrypt(NULL, k5key, KRB5_KEYUSAGE_KDC_REP_TICKET,
			     NULL, &in, &out);
	if (ret) {
	    free(out.ciphertext.data);
	    return KFAILURE;
	} else {
	    tkt->length = out.ciphertext.length;
	    memcpy(tkt->dat, out.ciphertext.data, out.ciphertext.length);
	    memset(out.ciphertext.data, 0, out.ciphertext.length);
	    free(out.ciphertext.data);
	}
    } else {
	key_sched(key,key_s);
	pcbc_encrypt((C_Block *)tkt->dat,(C_Block *)tkt->dat,
		     (long) tkt->length,key_s,(C_Block *)key,1);
    }
#endif /* !NOENCRYPTION */
    return 0;
}