xref: /illumos-gate/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/vfy_increds.c (revision 505d05c73a6e56769f263d4803b22eddd168ee24)
17c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
27c478bd9Sstevel@tonic-gate /*
37c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
47c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
57c478bd9Sstevel@tonic-gate  */
67c478bd9Sstevel@tonic-gate 
77c478bd9Sstevel@tonic-gate #include <k5-int.h>
8*505d05c7Sgtb #include "int-proto.h"
97c478bd9Sstevel@tonic-gate 
107c478bd9Sstevel@tonic-gate extern krb5_error_code krb5_libdefault_boolean();
117c478bd9Sstevel@tonic-gate 
127c478bd9Sstevel@tonic-gate static krb5_error_code
13*505d05c7Sgtb krb5_cc_copy_creds_except(krb5_context context, krb5_ccache incc, krb5_ccache outcc, krb5_principal princ)
147c478bd9Sstevel@tonic-gate {
157c478bd9Sstevel@tonic-gate    krb5_error_code code;
167c478bd9Sstevel@tonic-gate    krb5_flags flags;
177c478bd9Sstevel@tonic-gate    krb5_cc_cursor cur;
187c478bd9Sstevel@tonic-gate    krb5_creds creds;
197c478bd9Sstevel@tonic-gate 
207c478bd9Sstevel@tonic-gate    flags = 0;				/* turns off OPENCLOSE mode */
217c478bd9Sstevel@tonic-gate    if ((code = krb5_cc_set_flags(context, incc, flags)) != NULL)
227c478bd9Sstevel@tonic-gate       return(code);
237c478bd9Sstevel@tonic-gate    if ((code = krb5_cc_set_flags(context, outcc, flags)) != NULL)
247c478bd9Sstevel@tonic-gate       return(code);
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate    if ((code = krb5_cc_start_seq_get(context, incc, &cur)) != NULL)
277c478bd9Sstevel@tonic-gate       goto cleanup;
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate    while ((code = krb5_cc_next_cred(context, incc, &cur, &creds)) == NULL) {
307c478bd9Sstevel@tonic-gate       if (krb5_principal_compare(context, princ, creds.server))
317c478bd9Sstevel@tonic-gate 	 continue;
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate       code = krb5_cc_store_cred(context, outcc, &creds);
347c478bd9Sstevel@tonic-gate       krb5_free_cred_contents(context, &creds);
357c478bd9Sstevel@tonic-gate       if (code)
367c478bd9Sstevel@tonic-gate 	 goto cleanup;
377c478bd9Sstevel@tonic-gate    }
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate    if (code != KRB5_CC_END)
407c478bd9Sstevel@tonic-gate       goto cleanup;
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate    code = 0;
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate cleanup:
457c478bd9Sstevel@tonic-gate    flags = KRB5_TC_OPENCLOSE;
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate    if (code)
487c478bd9Sstevel@tonic-gate       (void) krb5_cc_set_flags(context, incc, flags);
497c478bd9Sstevel@tonic-gate    else
507c478bd9Sstevel@tonic-gate       code = krb5_cc_set_flags(context, incc, flags);
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate    if (code)
537c478bd9Sstevel@tonic-gate       (void) krb5_cc_set_flags(context, outcc, flags);
547c478bd9Sstevel@tonic-gate    else
557c478bd9Sstevel@tonic-gate       code = krb5_cc_set_flags(context, outcc, flags);
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate    return(code);
587c478bd9Sstevel@tonic-gate }
597c478bd9Sstevel@tonic-gate 
60*505d05c7Sgtb krb5_error_code KRB5_CALLCONV
617c478bd9Sstevel@tonic-gate krb5_verify_init_creds(krb5_context context,
627c478bd9Sstevel@tonic-gate 		       krb5_creds *creds,
637c478bd9Sstevel@tonic-gate 		       krb5_principal server_arg,
647c478bd9Sstevel@tonic-gate 		       krb5_keytab keytab_arg,
657c478bd9Sstevel@tonic-gate 		       krb5_ccache *ccache_arg,
667c478bd9Sstevel@tonic-gate 		       krb5_verify_init_creds_opt *options)
677c478bd9Sstevel@tonic-gate {
687c478bd9Sstevel@tonic-gate    krb5_error_code ret;
697c478bd9Sstevel@tonic-gate    krb5_principal server;
707c478bd9Sstevel@tonic-gate    krb5_keytab keytab;
717c478bd9Sstevel@tonic-gate    krb5_ccache ccache;
727c478bd9Sstevel@tonic-gate    krb5_keytab_entry kte;
737c478bd9Sstevel@tonic-gate    krb5_creds in_creds, *out_creds;
747c478bd9Sstevel@tonic-gate    krb5_auth_context authcon;
757c478bd9Sstevel@tonic-gate    krb5_data ap_req;
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate    /* KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN */
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate    server = NULL;
807c478bd9Sstevel@tonic-gate    keytab = NULL;
817c478bd9Sstevel@tonic-gate    ccache = NULL;
827c478bd9Sstevel@tonic-gate    out_creds = NULL;
837c478bd9Sstevel@tonic-gate    authcon = NULL;
847c478bd9Sstevel@tonic-gate    ap_req.data = NULL;
857c478bd9Sstevel@tonic-gate 
867c478bd9Sstevel@tonic-gate    if (server_arg) {
877c478bd9Sstevel@tonic-gate       server = server_arg;
887c478bd9Sstevel@tonic-gate    } else {
89*505d05c7Sgtb       if ((ret = krb5_sname_to_principal(context, NULL, NULL,
90*505d05c7Sgtb 					KRB5_NT_SRV_HST, &server))) {
917c478bd9Sstevel@tonic-gate 	goto cleanup;
927c478bd9Sstevel@tonic-gate       } else {
937c478bd9Sstevel@tonic-gate 	/*
947c478bd9Sstevel@tonic-gate 	 * Solaris Kerberos:
957c478bd9Sstevel@tonic-gate 	 * We check first up to see whether 'verify_ap_req_fail' is
967c478bd9Sstevel@tonic-gate 	 * set to false, because if FALSE there is no point in
977c478bd9Sstevel@tonic-gate 	 * proceeding any further with the strict TGT verification check
987c478bd9Sstevel@tonic-gate 	 * for the 'host/fqdn' service principal in the local keytab.
997c478bd9Sstevel@tonic-gate 	 */
1007c478bd9Sstevel@tonic-gate 	int nofail;
1017c478bd9Sstevel@tonic-gate         if (krb5_libdefault_boolean(context,
1027c478bd9Sstevel@tonic-gate 				&creds->client->realm,
1037c478bd9Sstevel@tonic-gate 				"verify_ap_req_nofail",
1047c478bd9Sstevel@tonic-gate 				&nofail) == 0) {
1057c478bd9Sstevel@tonic-gate 		/*
1067c478bd9Sstevel@tonic-gate 		 * Solaris Kerberos:
1077c478bd9Sstevel@tonic-gate 		 * If the administrator has configured the system such
1087c478bd9Sstevel@tonic-gate 		 * that its OK to fail this strict TGT verification check
1097c478bd9Sstevel@tonic-gate 		 * (i.e. verify_ap_req_nofail = false), set the
1107c478bd9Sstevel@tonic-gate 		 * 'ret' code to 0 and cleanup.
1117c478bd9Sstevel@tonic-gate 		 */
1127c478bd9Sstevel@tonic-gate 		if (!nofail) {
1137c478bd9Sstevel@tonic-gate 			ret = 0;
1147c478bd9Sstevel@tonic-gate 			goto cleanup;
1157c478bd9Sstevel@tonic-gate 		}
1167c478bd9Sstevel@tonic-gate 	}
1177c478bd9Sstevel@tonic-gate       }
1187c478bd9Sstevel@tonic-gate    }
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate    /* first, check if the server is in the keytab.  If not, there's
1217c478bd9Sstevel@tonic-gate       no reason to continue.  rd_req does all this, but there's
1227c478bd9Sstevel@tonic-gate       no way to know that a given error is caused by a missing
1237c478bd9Sstevel@tonic-gate       keytab or key, and not by some other problem. */
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate    if (keytab_arg) {
1267c478bd9Sstevel@tonic-gate       keytab = keytab_arg;
1277c478bd9Sstevel@tonic-gate    } else {
128*505d05c7Sgtb       if ((ret = krb5_kt_default(context, &keytab)))
1297c478bd9Sstevel@tonic-gate 	 goto cleanup;
1307c478bd9Sstevel@tonic-gate    }
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate    if ((ret = krb5_kt_get_entry(context, keytab, server, 0, 0, &kte)) != NULL) {
1337c478bd9Sstevel@tonic-gate        /* this means there is no keying material.  This is ok, as long as
1347c478bd9Sstevel@tonic-gate 	  it is not prohibited by the configuration */
1357c478bd9Sstevel@tonic-gate        if (options &&
1367c478bd9Sstevel@tonic-gate 	   (options->flags & KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL)) {
1377c478bd9Sstevel@tonic-gate 	   if (options->ap_req_nofail)
1387c478bd9Sstevel@tonic-gate 	       goto cleanup;
1397c478bd9Sstevel@tonic-gate        }
1407c478bd9Sstevel@tonic-gate    }
1417c478bd9Sstevel@tonic-gate 
1427c478bd9Sstevel@tonic-gate    krb5_kt_free_entry(context, &kte);
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate    /* If the creds are for the server principal, we're set, just do
1457c478bd9Sstevel@tonic-gate       a mk_req.	 Otherwise, do a get_credentials first. */
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate    if (krb5_principal_compare(context, server, creds->server)) {
1487c478bd9Sstevel@tonic-gate       /* make an ap_req */
149*505d05c7Sgtb       if ((ret = krb5_mk_req_extended(context, &authcon, 0, NULL, creds,
150*505d05c7Sgtb 				     &ap_req)))
1517c478bd9Sstevel@tonic-gate 	 goto cleanup;
1527c478bd9Sstevel@tonic-gate    } else {
1537c478bd9Sstevel@tonic-gate       /* this is unclean, but it's the easiest way without ripping the
1547c478bd9Sstevel@tonic-gate 	 library into very small pieces.  store the client's initial cred
1557c478bd9Sstevel@tonic-gate 	 in a memory ccache, then call the library.  Later, we'll copy
1567c478bd9Sstevel@tonic-gate 	 everything except the initial cred into the ccache we return to
1577c478bd9Sstevel@tonic-gate 	 the user.  A clean implementation would involve library
1587c478bd9Sstevel@tonic-gate 	 internals with a coherent idea of "in" and "out". */
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate       /* insert the initial cred into the ccache */
1617c478bd9Sstevel@tonic-gate 
162*505d05c7Sgtb       if ((ret = krb5_cc_resolve(context, "MEMORY:rd_req", &ccache)))
1637c478bd9Sstevel@tonic-gate 	 goto cleanup;
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate       if ((ret = krb5_cc_initialize(context, ccache, creds->client)) != NULL)
1667c478bd9Sstevel@tonic-gate 	 goto cleanup;
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate       if ((ret = krb5_cc_store_cred(context, ccache, creds)) != NULL)
1697c478bd9Sstevel@tonic-gate 	 goto cleanup;
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate       /* set up for get_creds */
1727c478bd9Sstevel@tonic-gate       memset(&in_creds, 0, sizeof(in_creds));
1737c478bd9Sstevel@tonic-gate       in_creds.client = creds->client;
1747c478bd9Sstevel@tonic-gate       in_creds.server = server;
175*505d05c7Sgtb       if ((ret = krb5_timeofday(context, &in_creds.times.endtime)))
1767c478bd9Sstevel@tonic-gate 	 goto cleanup;
1777c478bd9Sstevel@tonic-gate       in_creds.times.endtime += 5*60;
1787c478bd9Sstevel@tonic-gate 
179*505d05c7Sgtb       if ((ret = krb5_get_credentials(context, 0, ccache, &in_creds,
180*505d05c7Sgtb 				     &out_creds)))
1817c478bd9Sstevel@tonic-gate 	 goto cleanup;
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate       /* make an ap_req */
184*505d05c7Sgtb       if ((ret = krb5_mk_req_extended(context, &authcon, 0, NULL, out_creds,
185*505d05c7Sgtb 				     &ap_req)))
1867c478bd9Sstevel@tonic-gate 	 goto cleanup;
1877c478bd9Sstevel@tonic-gate    }
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate    /* wipe the auth context for mk_req */
1907c478bd9Sstevel@tonic-gate    if (authcon) {
1917c478bd9Sstevel@tonic-gate       krb5_auth_con_free(context, authcon);
1927c478bd9Sstevel@tonic-gate       authcon = NULL;
1937c478bd9Sstevel@tonic-gate    }
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate    /* verify the ap_req */
1967c478bd9Sstevel@tonic-gate 
197*505d05c7Sgtb    if ((ret = krb5_rd_req(context, &authcon, &ap_req, server, keytab,
198*505d05c7Sgtb 			 NULL, NULL)))
1997c478bd9Sstevel@tonic-gate       goto cleanup;
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate    /* if we get this far, then the verification succeeded.  We can
2027c478bd9Sstevel@tonic-gate       still fail if the library stuff here fails, but that's it */
2037c478bd9Sstevel@tonic-gate 
2047c478bd9Sstevel@tonic-gate    if (ccache_arg && ccache) {
2057c478bd9Sstevel@tonic-gate        if (*ccache_arg == NULL) {
2067c478bd9Sstevel@tonic-gate 	   krb5_ccache retcc;
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 	   retcc = NULL;
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate 	   if (((ret = krb5_cc_resolve(context, "MEMORY:rd_req2", &retcc)) != NULL) ||
2117c478bd9Sstevel@tonic-gate 	       ((ret = krb5_cc_initialize(context, retcc, creds->client)) != NULL) ||
2127c478bd9Sstevel@tonic-gate 	       ((ret = krb5_cc_copy_creds_except(context, ccache, retcc,
2137c478bd9Sstevel@tonic-gate 						creds->server)) != NULL)) {
2147c478bd9Sstevel@tonic-gate 	       if (retcc)
2157c478bd9Sstevel@tonic-gate 		   (void) krb5_cc_destroy(context, retcc);
2167c478bd9Sstevel@tonic-gate 	   } else {
2177c478bd9Sstevel@tonic-gate 	       *ccache_arg = retcc;
2187c478bd9Sstevel@tonic-gate 	   }
2197c478bd9Sstevel@tonic-gate        } else {
2207c478bd9Sstevel@tonic-gate 	   ret = krb5_cc_copy_creds_except(context, ccache, *ccache_arg,
2217c478bd9Sstevel@tonic-gate 					   server);
2227c478bd9Sstevel@tonic-gate        }
2237c478bd9Sstevel@tonic-gate    }
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate    /* if any of the above paths returned an errors, then ret is set
2267c478bd9Sstevel@tonic-gate       accordingly.  either that, or it's zero, which is fine, too */
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate cleanup:
2297c478bd9Sstevel@tonic-gate    if (!server_arg && server)
2307c478bd9Sstevel@tonic-gate       krb5_free_principal(context, server);
2317c478bd9Sstevel@tonic-gate    if (!keytab_arg && keytab)
2327c478bd9Sstevel@tonic-gate       (void) krb5_kt_close(context, keytab);
2337c478bd9Sstevel@tonic-gate    if (ccache)
2347c478bd9Sstevel@tonic-gate       (void) krb5_cc_destroy(context, ccache);
2357c478bd9Sstevel@tonic-gate    if (out_creds)
2367c478bd9Sstevel@tonic-gate       krb5_free_creds(context, out_creds);
2377c478bd9Sstevel@tonic-gate    if (authcon)
2387c478bd9Sstevel@tonic-gate       krb5_auth_con_free(context, authcon);
2397c478bd9Sstevel@tonic-gate    if (ap_req.data)
2407c478bd9Sstevel@tonic-gate       krb5_xfree(ap_req.data);
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate    return(ret);
2437c478bd9Sstevel@tonic-gate }
244