xref: /illumos-gate/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/vfy_increds.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
2*7c478bd9Sstevel@tonic-gate /*
3*7c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
4*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
5*7c478bd9Sstevel@tonic-gate  */
6*7c478bd9Sstevel@tonic-gate 
7*7c478bd9Sstevel@tonic-gate #include <k5-int.h>
8*7c478bd9Sstevel@tonic-gate 
9*7c478bd9Sstevel@tonic-gate extern krb5_error_code krb5_libdefault_boolean();
10*7c478bd9Sstevel@tonic-gate 
11*7c478bd9Sstevel@tonic-gate static krb5_error_code
12*7c478bd9Sstevel@tonic-gate krb5_cc_copy_creds_except(context, incc, outcc, princ)
13*7c478bd9Sstevel@tonic-gate      krb5_context context;
14*7c478bd9Sstevel@tonic-gate      krb5_ccache incc;
15*7c478bd9Sstevel@tonic-gate      krb5_ccache outcc;
16*7c478bd9Sstevel@tonic-gate      krb5_principal princ;
17*7c478bd9Sstevel@tonic-gate {
18*7c478bd9Sstevel@tonic-gate    krb5_error_code code;
19*7c478bd9Sstevel@tonic-gate    krb5_flags flags;
20*7c478bd9Sstevel@tonic-gate    krb5_cc_cursor cur;
21*7c478bd9Sstevel@tonic-gate    krb5_creds creds;
22*7c478bd9Sstevel@tonic-gate 
23*7c478bd9Sstevel@tonic-gate    flags = 0;				/* turns off OPENCLOSE mode */
24*7c478bd9Sstevel@tonic-gate    if ((code = krb5_cc_set_flags(context, incc, flags)) != NULL)
25*7c478bd9Sstevel@tonic-gate       return(code);
26*7c478bd9Sstevel@tonic-gate    if ((code = krb5_cc_set_flags(context, outcc, flags)) != NULL)
27*7c478bd9Sstevel@tonic-gate       return(code);
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate    if ((code = krb5_cc_start_seq_get(context, incc, &cur)) != NULL)
30*7c478bd9Sstevel@tonic-gate       goto cleanup;
31*7c478bd9Sstevel@tonic-gate 
32*7c478bd9Sstevel@tonic-gate    while ((code = krb5_cc_next_cred(context, incc, &cur, &creds)) == NULL) {
33*7c478bd9Sstevel@tonic-gate       if (krb5_principal_compare(context, princ, creds.server))
34*7c478bd9Sstevel@tonic-gate 	 continue;
35*7c478bd9Sstevel@tonic-gate 
36*7c478bd9Sstevel@tonic-gate       code = krb5_cc_store_cred(context, outcc, &creds);
37*7c478bd9Sstevel@tonic-gate       krb5_free_cred_contents(context, &creds);
38*7c478bd9Sstevel@tonic-gate       if (code)
39*7c478bd9Sstevel@tonic-gate 	 goto cleanup;
40*7c478bd9Sstevel@tonic-gate    }
41*7c478bd9Sstevel@tonic-gate 
42*7c478bd9Sstevel@tonic-gate    if (code != KRB5_CC_END)
43*7c478bd9Sstevel@tonic-gate       goto cleanup;
44*7c478bd9Sstevel@tonic-gate 
45*7c478bd9Sstevel@tonic-gate    code = 0;
46*7c478bd9Sstevel@tonic-gate 
47*7c478bd9Sstevel@tonic-gate cleanup:
48*7c478bd9Sstevel@tonic-gate    flags = KRB5_TC_OPENCLOSE;
49*7c478bd9Sstevel@tonic-gate 
50*7c478bd9Sstevel@tonic-gate    if (code)
51*7c478bd9Sstevel@tonic-gate       (void) krb5_cc_set_flags(context, incc, flags);
52*7c478bd9Sstevel@tonic-gate    else
53*7c478bd9Sstevel@tonic-gate       code = krb5_cc_set_flags(context, incc, flags);
54*7c478bd9Sstevel@tonic-gate 
55*7c478bd9Sstevel@tonic-gate    if (code)
56*7c478bd9Sstevel@tonic-gate       (void) krb5_cc_set_flags(context, outcc, flags);
57*7c478bd9Sstevel@tonic-gate    else
58*7c478bd9Sstevel@tonic-gate       code = krb5_cc_set_flags(context, outcc, flags);
59*7c478bd9Sstevel@tonic-gate 
60*7c478bd9Sstevel@tonic-gate    return(code);
61*7c478bd9Sstevel@tonic-gate }
62*7c478bd9Sstevel@tonic-gate 
63*7c478bd9Sstevel@tonic-gate KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
64*7c478bd9Sstevel@tonic-gate krb5_verify_init_creds(krb5_context context,
65*7c478bd9Sstevel@tonic-gate 		       krb5_creds *creds,
66*7c478bd9Sstevel@tonic-gate 		       krb5_principal server_arg,
67*7c478bd9Sstevel@tonic-gate 		       krb5_keytab keytab_arg,
68*7c478bd9Sstevel@tonic-gate 		       krb5_ccache *ccache_arg,
69*7c478bd9Sstevel@tonic-gate 		       krb5_verify_init_creds_opt *options)
70*7c478bd9Sstevel@tonic-gate {
71*7c478bd9Sstevel@tonic-gate    krb5_error_code ret;
72*7c478bd9Sstevel@tonic-gate    krb5_principal server;
73*7c478bd9Sstevel@tonic-gate    krb5_keytab keytab;
74*7c478bd9Sstevel@tonic-gate    krb5_ccache ccache;
75*7c478bd9Sstevel@tonic-gate    krb5_keytab_entry kte;
76*7c478bd9Sstevel@tonic-gate    krb5_creds in_creds, *out_creds;
77*7c478bd9Sstevel@tonic-gate    krb5_auth_context authcon;
78*7c478bd9Sstevel@tonic-gate    krb5_data ap_req;
79*7c478bd9Sstevel@tonic-gate 
80*7c478bd9Sstevel@tonic-gate    /* KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN */
81*7c478bd9Sstevel@tonic-gate 
82*7c478bd9Sstevel@tonic-gate    server = NULL;
83*7c478bd9Sstevel@tonic-gate    keytab = NULL;
84*7c478bd9Sstevel@tonic-gate    ccache = NULL;
85*7c478bd9Sstevel@tonic-gate    out_creds = NULL;
86*7c478bd9Sstevel@tonic-gate    authcon = NULL;
87*7c478bd9Sstevel@tonic-gate    ap_req.data = NULL;
88*7c478bd9Sstevel@tonic-gate 
89*7c478bd9Sstevel@tonic-gate    if (server_arg) {
90*7c478bd9Sstevel@tonic-gate       server = server_arg;
91*7c478bd9Sstevel@tonic-gate    } else {
92*7c478bd9Sstevel@tonic-gate       if (ret = krb5_sname_to_principal(context, NULL, NULL,
93*7c478bd9Sstevel@tonic-gate 					KRB5_NT_SRV_HST, &server)) {
94*7c478bd9Sstevel@tonic-gate 	goto cleanup;
95*7c478bd9Sstevel@tonic-gate       } else {
96*7c478bd9Sstevel@tonic-gate 	/*
97*7c478bd9Sstevel@tonic-gate 	 * Solaris Kerberos:
98*7c478bd9Sstevel@tonic-gate 	 * We check first up to see whether 'verify_ap_req_fail' is
99*7c478bd9Sstevel@tonic-gate 	 * set to false, because if FALSE there is no point in
100*7c478bd9Sstevel@tonic-gate 	 * proceeding any further with the strict TGT verification check
101*7c478bd9Sstevel@tonic-gate 	 * for the 'host/fqdn' service principal in the local keytab.
102*7c478bd9Sstevel@tonic-gate 	 */
103*7c478bd9Sstevel@tonic-gate 	int nofail;
104*7c478bd9Sstevel@tonic-gate         if (krb5_libdefault_boolean(context,
105*7c478bd9Sstevel@tonic-gate 				&creds->client->realm,
106*7c478bd9Sstevel@tonic-gate 				"verify_ap_req_nofail",
107*7c478bd9Sstevel@tonic-gate 				&nofail) == 0) {
108*7c478bd9Sstevel@tonic-gate 		/*
109*7c478bd9Sstevel@tonic-gate 		 * Solaris Kerberos:
110*7c478bd9Sstevel@tonic-gate 		 * If the administrator has configured the system such
111*7c478bd9Sstevel@tonic-gate 		 * that its OK to fail this strict TGT verification check
112*7c478bd9Sstevel@tonic-gate 		 * (i.e. verify_ap_req_nofail = false), set the
113*7c478bd9Sstevel@tonic-gate 		 * 'ret' code to 0 and cleanup.
114*7c478bd9Sstevel@tonic-gate 		 */
115*7c478bd9Sstevel@tonic-gate 		if (!nofail) {
116*7c478bd9Sstevel@tonic-gate 			ret = 0;
117*7c478bd9Sstevel@tonic-gate 			goto cleanup;
118*7c478bd9Sstevel@tonic-gate 		}
119*7c478bd9Sstevel@tonic-gate 	}
120*7c478bd9Sstevel@tonic-gate       }
121*7c478bd9Sstevel@tonic-gate    }
122*7c478bd9Sstevel@tonic-gate 
123*7c478bd9Sstevel@tonic-gate    /* first, check if the server is in the keytab.  If not, there's
124*7c478bd9Sstevel@tonic-gate       no reason to continue.  rd_req does all this, but there's
125*7c478bd9Sstevel@tonic-gate       no way to know that a given error is caused by a missing
126*7c478bd9Sstevel@tonic-gate       keytab or key, and not by some other problem. */
127*7c478bd9Sstevel@tonic-gate 
128*7c478bd9Sstevel@tonic-gate    if (keytab_arg) {
129*7c478bd9Sstevel@tonic-gate       keytab = keytab_arg;
130*7c478bd9Sstevel@tonic-gate    } else {
131*7c478bd9Sstevel@tonic-gate       if (ret = krb5_kt_default(context, &keytab))
132*7c478bd9Sstevel@tonic-gate 	 goto cleanup;
133*7c478bd9Sstevel@tonic-gate    }
134*7c478bd9Sstevel@tonic-gate 
135*7c478bd9Sstevel@tonic-gate    if ((ret = krb5_kt_get_entry(context, keytab, server, 0, 0, &kte)) != NULL) {
136*7c478bd9Sstevel@tonic-gate        /* this means there is no keying material.  This is ok, as long as
137*7c478bd9Sstevel@tonic-gate 	  it is not prohibited by the configuration */
138*7c478bd9Sstevel@tonic-gate        if (options &&
139*7c478bd9Sstevel@tonic-gate 	   (options->flags & KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL)) {
140*7c478bd9Sstevel@tonic-gate 	   if (options->ap_req_nofail)
141*7c478bd9Sstevel@tonic-gate 	       goto cleanup;
142*7c478bd9Sstevel@tonic-gate        }
143*7c478bd9Sstevel@tonic-gate    }
144*7c478bd9Sstevel@tonic-gate 
145*7c478bd9Sstevel@tonic-gate    krb5_kt_free_entry(context, &kte);
146*7c478bd9Sstevel@tonic-gate 
147*7c478bd9Sstevel@tonic-gate    /* If the creds are for the server principal, we're set, just do
148*7c478bd9Sstevel@tonic-gate       a mk_req.	 Otherwise, do a get_credentials first. */
149*7c478bd9Sstevel@tonic-gate 
150*7c478bd9Sstevel@tonic-gate    if (krb5_principal_compare(context, server, creds->server)) {
151*7c478bd9Sstevel@tonic-gate       /* make an ap_req */
152*7c478bd9Sstevel@tonic-gate       if (ret = krb5_mk_req_extended(context, &authcon, 0, NULL, creds,
153*7c478bd9Sstevel@tonic-gate 				     &ap_req))
154*7c478bd9Sstevel@tonic-gate 	 goto cleanup;
155*7c478bd9Sstevel@tonic-gate    } else {
156*7c478bd9Sstevel@tonic-gate       /* this is unclean, but it's the easiest way without ripping the
157*7c478bd9Sstevel@tonic-gate 	 library into very small pieces.  store the client's initial cred
158*7c478bd9Sstevel@tonic-gate 	 in a memory ccache, then call the library.  Later, we'll copy
159*7c478bd9Sstevel@tonic-gate 	 everything except the initial cred into the ccache we return to
160*7c478bd9Sstevel@tonic-gate 	 the user.  A clean implementation would involve library
161*7c478bd9Sstevel@tonic-gate 	 internals with a coherent idea of "in" and "out". */
162*7c478bd9Sstevel@tonic-gate 
163*7c478bd9Sstevel@tonic-gate       /* insert the initial cred into the ccache */
164*7c478bd9Sstevel@tonic-gate 
165*7c478bd9Sstevel@tonic-gate       if (ret = krb5_cc_resolve(context, "MEMORY:rd_req", &ccache))
166*7c478bd9Sstevel@tonic-gate 	 goto cleanup;
167*7c478bd9Sstevel@tonic-gate 
168*7c478bd9Sstevel@tonic-gate       if ((ret = krb5_cc_initialize(context, ccache, creds->client)) != NULL)
169*7c478bd9Sstevel@tonic-gate 	 goto cleanup;
170*7c478bd9Sstevel@tonic-gate 
171*7c478bd9Sstevel@tonic-gate       if ((ret = krb5_cc_store_cred(context, ccache, creds)) != NULL)
172*7c478bd9Sstevel@tonic-gate 	 goto cleanup;
173*7c478bd9Sstevel@tonic-gate 
174*7c478bd9Sstevel@tonic-gate       /* set up for get_creds */
175*7c478bd9Sstevel@tonic-gate       memset(&in_creds, 0, sizeof(in_creds));
176*7c478bd9Sstevel@tonic-gate       in_creds.client = creds->client;
177*7c478bd9Sstevel@tonic-gate       in_creds.server = server;
178*7c478bd9Sstevel@tonic-gate       if (ret = krb5_timeofday(context, &in_creds.times.endtime))
179*7c478bd9Sstevel@tonic-gate 	 goto cleanup;
180*7c478bd9Sstevel@tonic-gate       in_creds.times.endtime += 5*60;
181*7c478bd9Sstevel@tonic-gate 
182*7c478bd9Sstevel@tonic-gate       if (ret = krb5_get_credentials(context, 0, ccache, &in_creds,
183*7c478bd9Sstevel@tonic-gate 				     &out_creds))
184*7c478bd9Sstevel@tonic-gate 	 goto cleanup;
185*7c478bd9Sstevel@tonic-gate 
186*7c478bd9Sstevel@tonic-gate       /* make an ap_req */
187*7c478bd9Sstevel@tonic-gate       if (ret = krb5_mk_req_extended(context, &authcon, 0, NULL, out_creds,
188*7c478bd9Sstevel@tonic-gate 				     &ap_req))
189*7c478bd9Sstevel@tonic-gate 	 goto cleanup;
190*7c478bd9Sstevel@tonic-gate    }
191*7c478bd9Sstevel@tonic-gate 
192*7c478bd9Sstevel@tonic-gate    /* wipe the auth context for mk_req */
193*7c478bd9Sstevel@tonic-gate    if (authcon) {
194*7c478bd9Sstevel@tonic-gate       krb5_auth_con_free(context, authcon);
195*7c478bd9Sstevel@tonic-gate       authcon = NULL;
196*7c478bd9Sstevel@tonic-gate    }
197*7c478bd9Sstevel@tonic-gate 
198*7c478bd9Sstevel@tonic-gate    /* verify the ap_req */
199*7c478bd9Sstevel@tonic-gate 
200*7c478bd9Sstevel@tonic-gate    if (ret = krb5_rd_req(context, &authcon, &ap_req, server, keytab,
201*7c478bd9Sstevel@tonic-gate 			 NULL, NULL))
202*7c478bd9Sstevel@tonic-gate       goto cleanup;
203*7c478bd9Sstevel@tonic-gate 
204*7c478bd9Sstevel@tonic-gate    /* if we get this far, then the verification succeeded.  We can
205*7c478bd9Sstevel@tonic-gate       still fail if the library stuff here fails, but that's it */
206*7c478bd9Sstevel@tonic-gate 
207*7c478bd9Sstevel@tonic-gate    if (ccache_arg && ccache) {
208*7c478bd9Sstevel@tonic-gate        if (*ccache_arg == NULL) {
209*7c478bd9Sstevel@tonic-gate 	   krb5_ccache retcc;
210*7c478bd9Sstevel@tonic-gate 
211*7c478bd9Sstevel@tonic-gate 	   retcc = NULL;
212*7c478bd9Sstevel@tonic-gate 
213*7c478bd9Sstevel@tonic-gate 	   if (((ret = krb5_cc_resolve(context, "MEMORY:rd_req2", &retcc)) != NULL) ||
214*7c478bd9Sstevel@tonic-gate 	       ((ret = krb5_cc_initialize(context, retcc, creds->client)) != NULL) ||
215*7c478bd9Sstevel@tonic-gate 	       ((ret = krb5_cc_copy_creds_except(context, ccache, retcc,
216*7c478bd9Sstevel@tonic-gate 						creds->server)) != NULL)) {
217*7c478bd9Sstevel@tonic-gate 	       if (retcc)
218*7c478bd9Sstevel@tonic-gate 		   (void) krb5_cc_destroy(context, retcc);
219*7c478bd9Sstevel@tonic-gate 	   } else {
220*7c478bd9Sstevel@tonic-gate 	       *ccache_arg = retcc;
221*7c478bd9Sstevel@tonic-gate 	   }
222*7c478bd9Sstevel@tonic-gate        } else {
223*7c478bd9Sstevel@tonic-gate 	   ret = krb5_cc_copy_creds_except(context, ccache, *ccache_arg,
224*7c478bd9Sstevel@tonic-gate 					   server);
225*7c478bd9Sstevel@tonic-gate        }
226*7c478bd9Sstevel@tonic-gate    }
227*7c478bd9Sstevel@tonic-gate 
228*7c478bd9Sstevel@tonic-gate    /* if any of the above paths returned an errors, then ret is set
229*7c478bd9Sstevel@tonic-gate       accordingly.  either that, or it's zero, which is fine, too */
230*7c478bd9Sstevel@tonic-gate 
231*7c478bd9Sstevel@tonic-gate cleanup:
232*7c478bd9Sstevel@tonic-gate    if (!server_arg && server)
233*7c478bd9Sstevel@tonic-gate       krb5_free_principal(context, server);
234*7c478bd9Sstevel@tonic-gate    if (!keytab_arg && keytab)
235*7c478bd9Sstevel@tonic-gate       (void) krb5_kt_close(context, keytab);
236*7c478bd9Sstevel@tonic-gate    if (ccache)
237*7c478bd9Sstevel@tonic-gate       (void) krb5_cc_destroy(context, ccache);
238*7c478bd9Sstevel@tonic-gate    if (out_creds)
239*7c478bd9Sstevel@tonic-gate       krb5_free_creds(context, out_creds);
240*7c478bd9Sstevel@tonic-gate    if (authcon)
241*7c478bd9Sstevel@tonic-gate       krb5_auth_con_free(context, authcon);
242*7c478bd9Sstevel@tonic-gate    if (ap_req.data)
243*7c478bd9Sstevel@tonic-gate       krb5_xfree(ap_req.data);
244*7c478bd9Sstevel@tonic-gate 
245*7c478bd9Sstevel@tonic-gate    return(ret);
246*7c478bd9Sstevel@tonic-gate }
247