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