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