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