rd_req.c revision 90926
174462Salfred/*
274462Salfred * Copyright (c) 1997 - 2001 Kungliga Tekniska H�gskolan
3272850Shrs * (Royal Institute of Technology, Stockholm, Sweden).
4272850Shrs * All rights reserved.
58870Srgrimes *
6272850Shrs * Redistribution and use in source and binary forms, with or without
7272850Shrs * modification, are permitted provided that the following conditions
8272850Shrs * are met:
98870Srgrimes *
10272850Shrs * 1. Redistributions of source code must retain the above copyright
11272850Shrs *    notice, this list of conditions and the following disclaimer.
12272850Shrs *
13272850Shrs * 2. Redistributions in binary form must reproduce the above copyright
14272850Shrs *    notice, this list of conditions and the following disclaimer in the
15272850Shrs *    documentation and/or other materials provided with the distribution.
16272850Shrs *
17272850Shrs * 3. Neither the name of the Institute nor the names of its contributors
18272850Shrs *    may be used to endorse or promote products derived from this software
198870Srgrimes *    without specific prior written permission.
20272850Shrs *
21272850Shrs * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22272850Shrs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23272850Shrs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24272850Shrs * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25272850Shrs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26272850Shrs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27272850Shrs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28272850Shrs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29272850Shrs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30272850Shrs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31272850Shrs * SUCH DAMAGE.
321902Swollman */
331902Swollman
341902Swollman#include <krb5_locl.h>
35136582Sobrien
3692986SobrienRCSID("$Id: rd_req.c,v 1.47 2001/06/18 02:48:18 assar Exp $");
371902Swollman
3892986Sobrienstatic krb5_error_code
3992986Sobriendecrypt_tkt_enc_part (krb5_context context,
401902Swollman		      krb5_keyblock *key,
411902Swollman		      EncryptedData *enc_part,
421902Swollman		      EncTicketPart *decr_part)
431902Swollman{
441902Swollman    krb5_error_code ret;
451902Swollman    krb5_data plain;
461902Swollman    size_t len;
471902Swollman    krb5_crypto crypto;
4874462Salfred
4974462Salfred    ret = krb5_crypto_init(context, key, 0, &crypto);
50101067Snectar    if (ret)
511902Swollman	return ret;
521902Swollman    ret = krb5_decrypt_EncryptedData (context,
5311669Sphk				      crypto,
5474462Salfred				      KRB5_KU_TICKET,
551902Swollman				      enc_part,
561902Swollman				      &plain);
5774462Salfred    krb5_crypto_destroy(context, crypto);
581902Swollman    if (ret)
591902Swollman	return ret;
601902Swollman
611902Swollman    ret = krb5_decode_EncTicketPart(context, plain.data, plain.length,
621902Swollman				    decr_part, &len);
631902Swollman    krb5_data_free (&plain);
641902Swollman    return ret;
651902Swollman}
661902Swollman
671902Swollmanstatic krb5_error_code
6874462Salfreddecrypt_authenticator (krb5_context context,
691902Swollman		       EncryptionKey *key,
701902Swollman		       EncryptedData *enc_part,
711902Swollman		       Authenticator *authenticator,
721902Swollman		       krb5_key_usage usage)
731902Swollman{
741902Swollman    krb5_error_code ret;
7574462Salfred    krb5_data plain;
7674462Salfred    size_t len;
7774462Salfred    krb5_crypto crypto;
7874462Salfred
7974462Salfred    ret = krb5_crypto_init(context, key, 0, &crypto);
801902Swollman    if (ret)
811902Swollman	return ret;
82101045Sdarrenr    ret = krb5_decrypt_EncryptedData (context,
831902Swollman				      crypto,
841902Swollman				      usage /* KRB5_KU_AP_REQ_AUTH */,
851902Swollman				      enc_part,
86101147Snectar				      &plain);
87101045Sdarrenr    /* for backwards compatibility, also try the old usage */
881902Swollman    if (ret && usage == KRB5_KU_TGS_REQ_AUTH)
891902Swollman	ret = krb5_decrypt_EncryptedData (context,
901902Swollman					  crypto,
911902Swollman					  KRB5_KU_AP_REQ_AUTH,
921902Swollman					  enc_part,
931902Swollman					  &plain);
941902Swollman    krb5_crypto_destroy(context, crypto);
951902Swollman    if (ret)
961902Swollman	return ret;
971902Swollman
981902Swollman    ret = krb5_decode_Authenticator(context, plain.data, plain.length,
991902Swollman				    authenticator, &len);
1001902Swollman    krb5_data_free (&plain);
1011902Swollman    return ret;
1021902Swollman}
10374462Salfred
1041902Swollmankrb5_error_code
1051902Swollmankrb5_decode_ap_req(krb5_context context,
10621062Speter		   const krb5_data *inbuf,
1071902Swollman		   krb5_ap_req *ap_req)
1081902Swollman{
1091902Swollman    krb5_error_code ret;
1101902Swollman    size_t len;
11174462Salfred    ret = decode_AP_REQ(inbuf->data, inbuf->length, ap_req, &len);
11274462Salfred    if (ret)
11374462Salfred	return ret;
1141902Swollman    if (ap_req->pvno != 5){
11574462Salfred	free_AP_REQ(ap_req);
1161902Swollman	krb5_clear_error_string (context);
1171902Swollman	return KRB5KRB_AP_ERR_BADVERSION;
1181902Swollman    }
1191902Swollman    if (ap_req->msg_type != krb_ap_req){
12074462Salfred	free_AP_REQ(ap_req);
1211902Swollman	krb5_clear_error_string (context);
1221902Swollman	return KRB5KRB_AP_ERR_MSG_TYPE;
1231902Swollman    }
1241902Swollman    if (ap_req->ticket.tkt_vno != 5){
1251902Swollman	free_AP_REQ(ap_req);
1261902Swollman	krb5_clear_error_string (context);
1271902Swollman	return KRB5KRB_AP_ERR_BADVERSION;
1281902Swollman    }
1291902Swollman    return 0;
1301902Swollman}
1311902Swollman
1321902Swollmankrb5_error_code
1331902Swollmankrb5_decrypt_ticket(krb5_context context,
1341902Swollman		    Ticket *ticket,
1351902Swollman		    krb5_keyblock *key,
1361902Swollman		    EncTicketPart *out,
1371902Swollman		    krb5_flags flags)
1381902Swollman{
1391902Swollman    EncTicketPart t;
1401902Swollman    krb5_error_code ret;
1411902Swollman    ret = decrypt_tkt_enc_part (context, key, &ticket->enc_part, &t);
1421902Swollman    if (ret)
1431902Swollman	return ret;
1441902Swollman
1451902Swollman    {
14674462Salfred	krb5_timestamp now;
14774462Salfred	time_t start = t.authtime;
14874462Salfred
14974462Salfred	krb5_timeofday (context, &now);
15074462Salfred	if(t.starttime)
1511902Swollman	    start = *t.starttime;
15274462Salfred	if(start - now > context->max_skew
15374462Salfred	   || (t.flags.invalid
1541902Swollman	       && !(flags & KRB5_VERIFY_AP_REQ_IGNORE_INVALID))) {
1551902Swollman	    free_EncTicketPart(&t);
1561902Swollman	    krb5_clear_error_string (context);
157101045Sdarrenr	    return KRB5KRB_AP_ERR_TKT_NYV;
1581902Swollman	}
1591902Swollman	if(now - t.endtime > context->max_skew) {
1601902Swollman	    free_EncTicketPart(&t);
1611902Swollman	    krb5_clear_error_string (context);
16274462Salfred	    return KRB5KRB_AP_ERR_TKT_EXPIRED;
1631902Swollman	}
164    }
165
166    if(out)
167	*out = t;
168    else
169	free_EncTicketPart(&t);
170    return 0;
171}
172
173krb5_error_code
174krb5_verify_authenticator_checksum(krb5_context context,
175				   krb5_auth_context ac,
176				   void *data,
177				   size_t len)
178{
179    krb5_error_code ret;
180    krb5_keyblock *key;
181    krb5_authenticator authenticator;
182    krb5_crypto crypto;
183
184    ret = krb5_auth_con_getauthenticator (context,
185				      ac,
186				      &authenticator);
187    if(ret)
188	return ret;
189    if(authenticator->cksum == NULL)
190	return -17;
191    ret = krb5_auth_con_getkey(context, ac, &key);
192    if(ret) {
193	krb5_free_authenticator(context, &authenticator);
194	return ret;
195    }
196    ret = krb5_crypto_init(context, key, 0, &crypto);
197    if(ret)
198	goto out;
199    ret = krb5_verify_checksum (context,
200				crypto,
201				KRB5_KU_AP_REQ_AUTH_CKSUM,
202				data,
203				len,
204				authenticator->cksum);
205    krb5_crypto_destroy(context, crypto);
206out:
207    krb5_free_authenticator(context, &authenticator);
208    krb5_free_keyblock(context, key);
209    return ret;
210}
211
212#if 0
213static krb5_error_code
214check_transited(krb5_context context,
215		krb5_ticket *ticket)
216{
217    char **realms;
218    int num_realms;
219    krb5_error_code ret;
220
221    if(ticket->ticket.transited.tr_type != DOMAIN_X500_COMPRESS)
222	return KRB5KDC_ERR_TRTYPE_NOSUPP;
223
224    ret = krb5_domain_x500_decode(ticket->ticket.transited.contents,
225				  &realms, &num_realms,
226				  ticket->client->realm,
227				  ticket->server->realm);
228    if(ret)
229	return ret;
230    ret = krb5_check_transited_realms(context, realms, num_realms, NULL);
231    free(realms);
232    return ret;
233}
234#endif
235
236krb5_error_code
237krb5_verify_ap_req(krb5_context context,
238		   krb5_auth_context *auth_context,
239		   krb5_ap_req *ap_req,
240		   krb5_const_principal server,
241		   krb5_keyblock *keyblock,
242		   krb5_flags flags,
243		   krb5_flags *ap_req_options,
244		   krb5_ticket **ticket)
245{
246    return krb5_verify_ap_req2 (context,
247				auth_context,
248				ap_req,
249				server,
250				keyblock,
251				flags,
252				ap_req_options,
253				ticket,
254				KRB5_KU_AP_REQ_AUTH);
255}
256
257krb5_error_code
258krb5_verify_ap_req2(krb5_context context,
259		    krb5_auth_context *auth_context,
260		    krb5_ap_req *ap_req,
261		    krb5_const_principal server,
262		    krb5_keyblock *keyblock,
263		    krb5_flags flags,
264		    krb5_flags *ap_req_options,
265		    krb5_ticket **ticket,
266		    krb5_key_usage usage)
267{
268    krb5_ticket t;
269    krb5_auth_context ac;
270    krb5_error_code ret;
271
272    if (auth_context && *auth_context) {
273	ac = *auth_context;
274    } else {
275	ret = krb5_auth_con_init (context, &ac);
276	if (ret)
277	    return ret;
278    }
279
280    if (ap_req->ap_options.use_session_key && ac->keyblock){
281	ret = krb5_decrypt_ticket(context, &ap_req->ticket,
282				  ac->keyblock,
283				  &t.ticket,
284				  flags);
285	krb5_free_keyblock(context, ac->keyblock);
286	ac->keyblock = NULL;
287    }else
288	ret = krb5_decrypt_ticket(context, &ap_req->ticket,
289				  keyblock,
290				  &t.ticket,
291				  flags);
292
293    if(ret)
294	goto out;
295
296    principalname2krb5_principal(&t.server, ap_req->ticket.sname,
297				 ap_req->ticket.realm);
298    principalname2krb5_principal(&t.client, t.ticket.cname,
299				 t.ticket.crealm);
300
301    /* save key */
302
303    krb5_copy_keyblock(context, &t.ticket.key, &ac->keyblock);
304
305    ret = decrypt_authenticator (context,
306				 &t.ticket.key,
307				 &ap_req->authenticator,
308				 ac->authenticator,
309				 usage);
310    if (ret)
311	goto out2;
312
313    {
314	krb5_principal p1, p2;
315	krb5_boolean res;
316
317	principalname2krb5_principal(&p1,
318				     ac->authenticator->cname,
319				     ac->authenticator->crealm);
320	principalname2krb5_principal(&p2,
321				     t.ticket.cname,
322				     t.ticket.crealm);
323	res = krb5_principal_compare (context, p1, p2);
324	krb5_free_principal (context, p1);
325	krb5_free_principal (context, p2);
326	if (!res) {
327	    ret = KRB5KRB_AP_ERR_BADMATCH;
328	    krb5_clear_error_string (context);
329	    goto out2;
330	}
331    }
332
333    /* check addresses */
334
335    if (t.ticket.caddr
336	&& ac->remote_address
337	&& !krb5_address_search (context,
338				 ac->remote_address,
339				 t.ticket.caddr)) {
340	ret = KRB5KRB_AP_ERR_BADADDR;
341	krb5_clear_error_string (context);
342	goto out2;
343    }
344
345    if (ac->authenticator->seq_number)
346	krb5_auth_con_setremoteseqnumber(context, ac,
347					 *ac->authenticator->seq_number);
348
349    /* XXX - Xor sequence numbers */
350
351    if (ac->authenticator->subkey) {
352	ret = krb5_auth_con_setremotesubkey(context, ac,
353					    ac->authenticator->subkey);
354	if (ret)
355	    goto out2;
356    }
357
358    if (ap_req_options) {
359	*ap_req_options = 0;
360	if (ap_req->ap_options.use_session_key)
361	    *ap_req_options |= AP_OPTS_USE_SESSION_KEY;
362	if (ap_req->ap_options.mutual_required)
363	    *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
364    }
365
366    if(ticket){
367	*ticket = malloc(sizeof(**ticket));
368	**ticket = t;
369    } else
370	krb5_free_ticket (context, &t);
371    if (auth_context) {
372	if (*auth_context == NULL)
373	    *auth_context = ac;
374    } else
375	krb5_auth_con_free (context, ac);
376    return 0;
377 out2:
378    krb5_free_ticket (context, &t);
379 out:
380    if (auth_context == NULL || *auth_context == NULL)
381	krb5_auth_con_free (context, ac);
382    return ret;
383}
384
385
386krb5_error_code
387krb5_rd_req_with_keyblock(krb5_context context,
388			  krb5_auth_context *auth_context,
389			  const krb5_data *inbuf,
390			  krb5_const_principal server,
391			  krb5_keyblock *keyblock,
392			  krb5_flags *ap_req_options,
393			  krb5_ticket **ticket)
394{
395    krb5_error_code ret;
396    krb5_ap_req ap_req;
397
398    if (*auth_context == NULL) {
399	ret = krb5_auth_con_init(context, auth_context);
400	if (ret)
401	    return ret;
402    }
403
404    ret = krb5_decode_ap_req(context, inbuf, &ap_req);
405    if(ret)
406	return ret;
407
408    ret = krb5_verify_ap_req(context,
409			     auth_context,
410			     &ap_req,
411			     server,
412			     keyblock,
413			     0,
414			     ap_req_options,
415			     ticket);
416
417    free_AP_REQ(&ap_req);
418    return ret;
419}
420
421static krb5_error_code
422get_key_from_keytab(krb5_context context,
423		    krb5_auth_context *auth_context,
424		    krb5_ap_req *ap_req,
425		    krb5_const_principal server,
426		    krb5_keytab keytab,
427		    krb5_keyblock **out_key)
428{
429    krb5_keytab_entry entry;
430    krb5_error_code ret;
431    int kvno;
432    krb5_keytab real_keytab;
433
434    if(keytab == NULL)
435	krb5_kt_default(context, &real_keytab);
436    else
437	real_keytab = keytab;
438
439    if (ap_req->ticket.enc_part.kvno)
440	kvno = *ap_req->ticket.enc_part.kvno;
441    else
442	kvno = 0;
443
444    ret = krb5_kt_get_entry (context,
445			     real_keytab,
446			     server,
447			     kvno,
448			     ap_req->ticket.enc_part.etype,
449			     &entry);
450    if(ret)
451	goto out;
452    ret = krb5_copy_keyblock(context, &entry.keyblock, out_key);
453    krb5_kt_free_entry (context, &entry);
454out:
455    if(keytab == NULL)
456	krb5_kt_close(context, real_keytab);
457
458    return ret;
459}
460
461krb5_error_code
462krb5_rd_req(krb5_context context,
463	    krb5_auth_context *auth_context,
464	    const krb5_data *inbuf,
465	    krb5_const_principal server,
466	    krb5_keytab keytab,
467	    krb5_flags *ap_req_options,
468	    krb5_ticket **ticket)
469{
470    krb5_error_code ret;
471    krb5_ap_req ap_req;
472    krb5_keyblock *keyblock = NULL;
473    krb5_principal service = NULL;
474
475    if (*auth_context == NULL) {
476	ret = krb5_auth_con_init(context, auth_context);
477	if (ret)
478	    return ret;
479    }
480
481    ret = krb5_decode_ap_req(context, inbuf, &ap_req);
482    if(ret)
483	return ret;
484
485    if(server == NULL){
486	principalname2krb5_principal(&service,
487				     ap_req.ticket.sname,
488				     ap_req.ticket.realm);
489	server = service;
490    }
491
492    if(ap_req.ap_options.use_session_key == 0 ||
493       (*auth_context)->keyblock == NULL){
494	ret = get_key_from_keytab(context,
495				  auth_context,
496				  &ap_req,
497				  server,
498				  keytab,
499				  &keyblock);
500	if(ret)
501	    goto out;
502    }
503
504
505    ret = krb5_verify_ap_req(context,
506			     auth_context,
507			     &ap_req,
508			     server,
509			     keyblock,
510			     0,
511			     ap_req_options,
512			     ticket);
513
514    if(keyblock != NULL)
515	krb5_free_keyblock(context, keyblock);
516
517out:
518    free_AP_REQ(&ap_req);
519    if(service)
520	krb5_free_principal(context, service);
521    return ret;
522}
523