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