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