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