xref: /titanic_52/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/auth_con.c (revision 159d09a20817016f09b3ea28d1bdada4a336bb91)
1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 
7 #include "k5-int.h"
8 #include "auth_con.h"
9 
10 static krb5_boolean chk_heimdal_seqnum(krb5_ui_4, krb5_ui_4);
11 
12 /*ARGSUSED*/
13 static krb5_error_code
14 actx_copy_addr(krb5_context context, const krb5_address *inad, krb5_address **outad)
15 {
16     krb5_address *tmpad;
17 
18     if (!(tmpad = (krb5_address *)malloc(sizeof(*tmpad))))
19 	return ENOMEM;
20     *tmpad = *inad;
21     if (!(tmpad->contents = (krb5_octet *)malloc(inad->length))) {
22 	krb5_xfree(tmpad);
23 	return ENOMEM;
24     }
25     /* Solaris Kerberos */
26     (void) memcpy((char *)tmpad->contents, (char *)inad->contents, inad->length);
27     *outad = tmpad;
28     return 0;
29 }
30 
31 krb5_error_code KRB5_CALLCONV
32 krb5_auth_con_init(krb5_context context, krb5_auth_context *auth_context)
33 {
34     *auth_context =
35             (krb5_auth_context)MALLOC(sizeof(struct _krb5_auth_context));
36     if (!*auth_context)
37 	    return ENOMEM;
38 
39     /* Solaris Kerberos */
40     (void) memset(*auth_context, 0, sizeof(struct _krb5_auth_context));
41 
42     /* Default flags, do time not seq */
43     (*auth_context)->auth_context_flags =
44 	    KRB5_AUTH_CONTEXT_DO_TIME |  KRB5_AUTH_CONN_INITIALIZED;
45 
46     (*auth_context)->req_cksumtype = context->default_ap_req_sumtype;
47     (*auth_context)->safe_cksumtype = context->default_safe_sumtype;
48     (*auth_context) -> checksum_func = NULL;
49     (*auth_context)->checksum_func_data = NULL;
50     (*auth_context)->magic = KV5M_AUTH_CONTEXT;
51     return 0;
52 }
53 
54 krb5_error_code KRB5_CALLCONV
55 krb5_auth_con_free(krb5_context context, krb5_auth_context auth_context)
56 {
57     if (auth_context->local_addr)
58 	krb5_free_address(context, auth_context->local_addr);
59     if (auth_context->remote_addr)
60 	krb5_free_address(context, auth_context->remote_addr);
61     if (auth_context->local_port)
62 	krb5_free_address(context, auth_context->local_port);
63     if (auth_context->remote_port)
64 	krb5_free_address(context, auth_context->remote_port);
65     if (auth_context->authentp)
66 	krb5_free_authenticator(context, auth_context->authentp);
67     if (auth_context->keyblock)
68 	krb5_free_keyblock(context, auth_context->keyblock);
69     if (auth_context->send_subkey)
70 	krb5_free_keyblock(context, auth_context->send_subkey);
71     if (auth_context->recv_subkey)
72 	krb5_free_keyblock(context, auth_context->recv_subkey);
73     /* Solaris Kerberos */
74     if (auth_context->rcache)
75 	(void) krb5_rc_close(context, auth_context->rcache);
76     if (auth_context->permitted_etypes)
77 	krb5_xfree(auth_context->permitted_etypes);
78     free(auth_context);
79     return 0;
80 }
81 
82 krb5_error_code
83 krb5_auth_con_setaddrs(krb5_context context, krb5_auth_context auth_context, krb5_address *local_addr, krb5_address *remote_addr)
84 {
85     krb5_error_code	retval;
86 
87     /* Free old addresses */
88     if (auth_context->local_addr)
89 	(void) krb5_free_address(context, auth_context->local_addr);
90     if (auth_context->remote_addr)
91 	(void) krb5_free_address(context, auth_context->remote_addr);
92 
93     retval = 0;
94     if (local_addr)
95 	retval = actx_copy_addr(context,
96 				local_addr,
97 				&auth_context->local_addr);
98     else
99 	auth_context->local_addr = NULL;
100 
101     if (!retval && remote_addr)
102 	retval = actx_copy_addr(context,
103 				remote_addr,
104 				&auth_context->remote_addr);
105     else
106 	auth_context->remote_addr = NULL;
107 
108     return retval;
109 }
110 
111 krb5_error_code KRB5_CALLCONV
112 krb5_auth_con_getaddrs(krb5_context context, krb5_auth_context auth_context, krb5_address **local_addr, krb5_address **remote_addr)
113 {
114     krb5_error_code	retval;
115 
116     retval = 0;
117     if (local_addr && auth_context->local_addr) {
118 	retval = actx_copy_addr(context,
119 				auth_context->local_addr,
120 				local_addr);
121     }
122     if (!retval && (remote_addr) && auth_context->remote_addr) {
123 	retval = actx_copy_addr(context,
124 				auth_context->remote_addr,
125 				remote_addr);
126     }
127     return retval;
128 }
129 
130 krb5_error_code KRB5_CALLCONV
131 krb5_auth_con_setports(krb5_context context, krb5_auth_context auth_context, krb5_address *local_port, krb5_address *remote_port)
132 {
133     krb5_error_code	retval;
134 
135     /* Free old addresses */
136     if (auth_context->local_port)
137 	(void) krb5_free_address(context, auth_context->local_port);
138     if (auth_context->remote_port)
139 	(void) krb5_free_address(context, auth_context->remote_port);
140 
141     retval = 0;
142     if (local_port)
143 	retval = actx_copy_addr(context,
144 				local_port,
145 				&auth_context->local_port);
146     else
147 	auth_context->local_port = NULL;
148 
149     if (!retval && remote_port)
150 	retval = actx_copy_addr(context,
151 				remote_port,
152 				&auth_context->remote_port);
153     else
154 	auth_context->remote_port = NULL;
155 
156     return retval;
157 }
158 
159 
160 /*
161  * This function overloads the keyblock field. It is only useful prior to
162  * a krb5_rd_req_decode() call for user to user authentication where the
163  * server has the key and needs to use it to decrypt the incoming request.
164  * Once decrypted this key is no longer necessary and is then overwritten
165  * with the session key sent by the client.
166  */
167 krb5_error_code KRB5_CALLCONV
168 krb5_auth_con_setuseruserkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock *keyblock)
169 {
170     if (auth_context->keyblock)
171 	krb5_free_keyblock(context, auth_context->keyblock);
172     return(krb5_copy_keyblock(context, keyblock, &(auth_context->keyblock)));
173 }
174 
175 krb5_error_code KRB5_CALLCONV
176 krb5_auth_con_getkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock **keyblock)
177 {
178     if (auth_context->keyblock)
179     	return krb5_copy_keyblock(context, auth_context->keyblock, keyblock);
180     *keyblock = NULL;
181     return 0;
182 }
183 
184 krb5_error_code KRB5_CALLCONV
185 krb5_auth_con_getlocalsubkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock **keyblock)
186 {
187     return krb5_auth_con_getsendsubkey(context, auth_context, keyblock);
188 }
189 
190 krb5_error_code KRB5_CALLCONV
191 krb5_auth_con_getremotesubkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock **keyblock)
192 {
193     return krb5_auth_con_getrecvsubkey(context, auth_context, keyblock);
194 }
195 
196 krb5_error_code KRB5_CALLCONV
197 krb5_auth_con_setsendsubkey(krb5_context ctx, krb5_auth_context ac, krb5_keyblock *keyblock)
198 {
199     if (ac->send_subkey != NULL)
200 	krb5_free_keyblock(ctx, ac->send_subkey);
201     ac->send_subkey = NULL;
202     if (keyblock !=NULL)
203 	return krb5_copy_keyblock(ctx, keyblock, &ac->send_subkey);
204     else
205 	return 0;
206 }
207 
208 krb5_error_code KRB5_CALLCONV
209 krb5_auth_con_setrecvsubkey(krb5_context ctx, krb5_auth_context ac, krb5_keyblock *keyblock)
210 {
211     if (ac->recv_subkey != NULL)
212 	krb5_free_keyblock(ctx, ac->recv_subkey);
213     ac->recv_subkey = NULL;
214     if (keyblock != NULL)
215 	return krb5_copy_keyblock(ctx, keyblock, &ac->recv_subkey);
216     else
217 	return 0;
218 }
219 
220 krb5_error_code KRB5_CALLCONV
221 krb5_auth_con_getsendsubkey(krb5_context ctx, krb5_auth_context ac, krb5_keyblock **keyblock)
222 {
223     if (ac->send_subkey != NULL)
224 	return krb5_copy_keyblock(ctx, ac->send_subkey, keyblock);
225     *keyblock = NULL;
226     return 0;
227 }
228 
229 krb5_error_code KRB5_CALLCONV
230 krb5_auth_con_getrecvsubkey(krb5_context ctx, krb5_auth_context ac, krb5_keyblock **keyblock)
231 {
232     if (ac->recv_subkey != NULL)
233 	return krb5_copy_keyblock(ctx, ac->recv_subkey, keyblock);
234     *keyblock = NULL;
235     return 0;
236 }
237 
238 /*ARGSUSED*/
239 krb5_error_code KRB5_CALLCONV
240 krb5_auth_con_set_req_cksumtype(krb5_context context, krb5_auth_context auth_context, krb5_cksumtype cksumtype)
241 {
242     auth_context->req_cksumtype = cksumtype;
243     return 0;
244 }
245 
246 /*ARGSUSED*/
247 krb5_error_code
248 krb5_auth_con_set_safe_cksumtype(krb5_context context, krb5_auth_context auth_context, krb5_cksumtype cksumtype)
249 {
250     auth_context->safe_cksumtype = cksumtype;
251     return 0;
252 }
253 
254 /*ARGSUSED*/
255 krb5_error_code KRB5_CALLCONV
256 krb5_auth_con_getlocalseqnumber(krb5_context context, krb5_auth_context auth_context, krb5_int32 *seqnumber)
257 {
258     *seqnumber = auth_context->local_seq_number;
259     return 0;
260 }
261 
262 krb5_error_code KRB5_CALLCONV
263 krb5_auth_con_getauthenticator(krb5_context context, krb5_auth_context auth_context, krb5_authenticator **authenticator)
264 {
265     return (krb5_copy_authenticator(context, auth_context->authentp,
266 				    authenticator));
267 }
268 
269 /*ARGSUSED*/
270 krb5_error_code KRB5_CALLCONV
271 krb5_auth_con_getremoteseqnumber(krb5_context context, krb5_auth_context auth_context, krb5_int32 *seqnumber)
272 {
273     *seqnumber = auth_context->remote_seq_number;
274     return 0;
275 }
276 
277 krb5_error_code KRB5_CALLCONV
278 krb5_auth_con_initivector(krb5_context context, krb5_auth_context auth_context)
279 {
280     krb5_error_code ret;
281 
282     if (auth_context->keyblock) {
283 	size_t blocksize;
284 
285 	if ((ret = krb5_c_block_size(context, auth_context->keyblock->enctype,
286 				    &blocksize)))
287 	    return(ret);
288 	if ((auth_context->i_vector = (krb5_pointer)malloc(blocksize))) {
289 	    memset(auth_context->i_vector, 0, blocksize);
290 	    return 0;
291 	}
292 	return ENOMEM;
293     }
294     return EINVAL; /* XXX need an error for no keyblock */
295 }
296 
297 /*ARGSUSED*/
298 krb5_error_code
299 krb5_auth_con_setivector(krb5_context context, krb5_auth_context auth_context, krb5_pointer ivector)
300 {
301     auth_context->i_vector = ivector;
302     return 0;
303 }
304 
305 /*ARGSUSED*/
306 krb5_error_code
307 krb5_auth_con_getivector(krb5_context context, krb5_auth_context auth_context, krb5_pointer *ivector)
308 {
309     *ivector = auth_context->i_vector;
310     return 0;
311 }
312 
313 /*ARGSUSED*/
314 krb5_error_code KRB5_CALLCONV
315 krb5_auth_con_setflags(krb5_context context, krb5_auth_context auth_context, krb5_int32 flags)
316 {
317     auth_context->auth_context_flags = flags;
318     return 0;
319 }
320 
321 /*ARGSUSED*/
322 krb5_error_code KRB5_CALLCONV
323 krb5_auth_con_getflags(krb5_context context, krb5_auth_context auth_context, krb5_int32 *flags)
324 {
325     *flags = auth_context->auth_context_flags;
326     return 0;
327 }
328 
329 /*ARGSUSED*/
330 krb5_error_code KRB5_CALLCONV
331 krb5_auth_con_setrcache(krb5_context context, krb5_auth_context auth_context, krb5_rcache rcache)
332 {
333     auth_context->rcache = rcache;
334     return 0;
335 }
336 
337 /*ARGSUSED*/
338 krb5_error_code
339 krb5_auth_con_getrcache(krb5_context context, krb5_auth_context auth_context, krb5_rcache *rcache)
340 {
341     *rcache = auth_context->rcache;
342     return 0;
343 }
344 
345 /*ARGSUSED*/
346 krb5_error_code
347 krb5_auth_con_setpermetypes(krb5_context context, krb5_auth_context auth_context, const krb5_enctype *permetypes)
348 {
349     krb5_enctype	* newpe;
350     int i;
351 
352     for (i=0; permetypes[i]; i++)
353 	;
354     i++; /* include the zero */
355 
356     if ((newpe = (krb5_enctype *) malloc(i*sizeof(krb5_enctype)))
357 	== NULL)
358 	return(ENOMEM);
359 
360     if (auth_context->permitted_etypes)
361 	krb5_xfree(auth_context->permitted_etypes);
362 
363     auth_context->permitted_etypes = newpe;
364 
365     /* Solaris Kerberos */
366     (void) memcpy(newpe, permetypes, i*sizeof(krb5_enctype));
367 
368     return 0;
369 }
370 
371 /*ARGSUSED*/
372 krb5_error_code
373 krb5_auth_con_getpermetypes(krb5_context context, krb5_auth_context auth_context, krb5_enctype **permetypes)
374 {
375     krb5_enctype	* newpe;
376     int i;
377 
378     if (! auth_context->permitted_etypes) {
379 	*permetypes = NULL;
380 	return(0);
381     }
382 
383     for (i=0; auth_context->permitted_etypes[i]; i++)
384 	;
385     i++; /* include the zero */
386 
387     if ((newpe = (krb5_enctype *) malloc(i*sizeof(krb5_enctype)))
388 	== NULL)
389 	return(ENOMEM);
390 
391     *permetypes = newpe;
392 
393     memcpy(newpe, auth_context->permitted_etypes, i*sizeof(krb5_enctype));
394 
395     return(0);
396 }
397 
398 krb5_error_code KRB5_CALLCONV
399 krb5_auth_con_set_checksum_func( krb5_context context,
400 				 krb5_auth_context  auth_context,
401 				 krb5_mk_req_checksum_func func,
402 				 void *data)
403 {
404   auth_context->checksum_func = func;
405   auth_context->checksum_func_data = data;
406   return 0;
407 }
408 
409 krb5_error_code KRB5_CALLCONV
410 krb5_auth_con_get_checksum_func( krb5_context context,
411 				 krb5_auth_context auth_context,
412 				 krb5_mk_req_checksum_func *func,
413 				 void **data)
414 {
415   *func = auth_context->checksum_func;
416   *data = auth_context->checksum_func_data;
417   return 0;
418 }
419 
420 /*
421  * krb5int_auth_con_chkseqnum
422  *
423  * We use a somewhat complex heuristic for validating received
424  * sequence numbers.  We must accommodate both our older
425  * implementation, which sends negative sequence numbers, and the
426  * broken Heimdal implementation (at least as of 0.5.2), which
427  * violates X.690 BER for integer encodings.  The requirement of
428  * handling negative sequence numbers removes one of easier means of
429  * detecting a Heimdal implementation, so we resort to this mess
430  * here.
431  *
432  * X.690 BER (and consequently DER, which are the required encoding
433  * rules in RFC1510) encode all integer types as signed integers.
434  * This means that the MSB being set on the first octet of the
435  * contents of the encoding indicates a negative value.  Heimdal does
436  * not prepend the required zero octet to unsigned integer encodings
437  * which would otherwise have the MSB of the first octet of their
438  * encodings set.
439  *
440  * Our ASN.1 library implements a special decoder for sequence
441  * numbers, accepting both negative and positive 32-bit numbers but
442  * mapping them both into the space of positive unsigned 32-bit
443  * numbers in the obvious bit-pattern-preserving way.  This maintains
444  * compatibility with our older implementations.  This also means that
445  * encodings emitted by Heimdal are ambiguous.
446  *
447  * Heimdal counter value	received uint32 value
448  *
449  * 0x00000080			0xFFFFFF80
450  * 0x000000FF			0xFFFFFFFF
451  * 0x00008000			0xFFFF8000
452  * 0x0000FFFF			0xFFFFFFFF
453  * 0x00800000			0xFF800000
454  * 0x00FFFFFF			0xFFFFFFFF
455  * 0xFF800000			0xFF800000
456  * 0xFFFFFFFF			0xFFFFFFFF
457  *
458  * We use two auth_context flags, SANE_SEQ and HEIMDAL_SEQ, which are
459  * only set after we can unambiguously determine the sanity of the
460  * sending implementation.  Once one of these flags is set, we accept
461  * only the sequence numbers appropriate to the remote implementation
462  * type.  We can make the determination in two different ways.  The
463  * first is to note the receipt of a "negative" sequence number when a
464  * "positive" one was expected.  The second is to note the receipt of
465  * a sequence number that wraps through "zero" in a weird way.  The
466  * latter corresponds to the receipt of an initial sequence number in
467  * the ambiguous range.
468  *
469  * There are 2^7 + 2^15 + 2^23 + 2^23 = 16810112 total ambiguous
470  * initial Heimdal counter values, but we receive them as one of 2^23
471  * possible values.  There is a ~1/256 chance of a Heimdal
472  * implementation sending an intial sequence number in the ambiguous
473  * range.
474  *
475  * We have to do special treatment when receiving sequence numbers
476  * between 0xFF800000..0xFFFFFFFF, or when wrapping through zero
477  * weirdly (due to ambiguous initial sequence number).  If we are
478  * expecting a value corresponding to an ambiguous Heimdal counter
479  * value, and we receive an exact match, we can mark the remote end as
480  * sane.
481  */
482 krb5_boolean
483 krb5int_auth_con_chkseqnum(
484     krb5_context ctx,
485     krb5_auth_context ac,
486     krb5_ui_4 in_seq)
487 {
488     krb5_ui_4 exp_seq;
489 
490     exp_seq = ac->remote_seq_number;
491 
492     /*
493      * If sender is known to be sane, accept _only_ exact matches.
494      */
495     if (ac->auth_context_flags & KRB5_AUTH_CONN_SANE_SEQ)
496 	return in_seq == exp_seq;
497 
498     /*
499      * If sender is not known to be sane, first check the ambiguous
500      * range of received values, 0xFF800000..0xFFFFFFFF.
501      */
502     if ((in_seq & 0xFF800000) == 0xFF800000) {
503 	/*
504 	 * If expected sequence number is in the range
505 	 * 0xFF800000..0xFFFFFFFF, then we can't make any
506 	 * determinations about the sanity of the sending
507 	 * implementation.
508 	 */
509 	if ((exp_seq & 0xFF800000) == 0xFF800000 && in_seq == exp_seq)
510 	    return 1;
511 	/*
512 	 * If sender is not known for certain to be a broken Heimdal
513 	 * implementation, check for exact match.
514 	 */
515 	if (!(ac->auth_context_flags & KRB5_AUTH_CONN_HEIMDAL_SEQ)
516 	    && in_seq == exp_seq)
517 	    return 1;
518 	/*
519 	 * Now apply hairy algorithm for matching sequence numbers
520 	 * sent by broken Heimdal implementations.  If it matches, we
521 	 * know for certain it's a broken Heimdal sender.
522 	 */
523 	if (chk_heimdal_seqnum(exp_seq, in_seq)) {
524 	    ac->auth_context_flags |= KRB5_AUTH_CONN_HEIMDAL_SEQ;
525 	    return 1;
526 	}
527 	return 0;
528     }
529 
530     /*
531      * Received value not in the ambiguous range?  If the _expected_
532      * value is in the range of ambiguous Hemidal counter values, and
533      * it matches the received value, sender is known to be sane.
534      */
535     if (in_seq == exp_seq) {
536 	if ((   exp_seq & 0xFFFFFF80) == 0x00000080
537 	    || (exp_seq & 0xFFFF8000) == 0x00008000
538 	    || (exp_seq & 0xFF800000) == 0x00800000)
539 	    ac->auth_context_flags |= KRB5_AUTH_CONN_SANE_SEQ;
540 	return 1;
541     }
542 
543     /*
544      * Magic wraparound for the case where the intial sequence number
545      * is in the ambiguous range.  This means that the sender's
546      * counter is at a different count than ours, so we correct ours,
547      * and mark the sender as being a broken Heimdal implementation.
548      */
549     if (exp_seq == 0
550 	&& !(ac->auth_context_flags & KRB5_AUTH_CONN_HEIMDAL_SEQ)) {
551 	switch (in_seq) {
552 	case 0x100:
553 	case 0x10000:
554 	case 0x1000000:
555 	    ac->auth_context_flags |= KRB5_AUTH_CONN_HEIMDAL_SEQ;
556 	    exp_seq = in_seq;
557 	    return 1;
558 	default:
559 	    return 0;
560 	}
561     }
562     return 0;
563 }
564 
565 static krb5_boolean
566 chk_heimdal_seqnum(krb5_ui_4 exp_seq, krb5_ui_4 in_seq)
567 {
568     if (( exp_seq & 0xFF800000) == 0x00800000
569 	&& (in_seq & 0xFF800000) == 0xFF800000
570 	&& (in_seq & 0x00FFFFFF) == exp_seq)
571 	return 1;
572     else if ((  exp_seq & 0xFFFF8000) == 0x00008000
573 	     && (in_seq & 0xFFFF8000) == 0xFFFF8000
574 	     && (in_seq & 0x0000FFFF) == exp_seq)
575 	return 1;
576     else if ((  exp_seq & 0xFFFFFF80) == 0x00000080
577 	     && (in_seq & 0xFFFFFF80) == 0xFFFFFF80
578 	     && (in_seq & 0x000000FF) == exp_seq)
579 	return 1;
580     else
581 	return 0;
582 }
583