1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/gssapi/krb5/store_cred.c */
3 /*
4  * Copyright 2009 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 
27 #include "k5-int.h"
28 #include "gssapiP_krb5.h"
29 
30 static int
has_unexpired_creds(krb5_gss_cred_id_t kcred,const gss_OID desired_mech,int default_cred,gss_const_key_value_set_t cred_store)31 has_unexpired_creds(krb5_gss_cred_id_t kcred,
32                     const gss_OID desired_mech,
33                     int default_cred,
34                     gss_const_key_value_set_t cred_store)
35 {
36     OM_uint32 major_status, minor;
37     gss_name_t cred_name;
38     gss_OID_set_desc desired_mechs;
39     gss_cred_id_t tmp_cred = GSS_C_NO_CREDENTIAL;
40     OM_uint32 time_rec;
41 
42     desired_mechs.count = 1;
43     desired_mechs.elements = (gss_OID)desired_mech;
44 
45     if (default_cred)
46         cred_name = GSS_C_NO_NAME;
47     else
48         cred_name = (gss_name_t)kcred->name;
49 
50     major_status = krb5_gss_acquire_cred_from(&minor, cred_name, 0,
51                                               &desired_mechs, GSS_C_INITIATE,
52                                               cred_store, &tmp_cred, NULL,
53                                               &time_rec);
54 
55     krb5_gss_release_cred(&minor, &tmp_cred);
56 
57     return (GSS_ERROR(major_status) || time_rec);
58 }
59 
60 static OM_uint32
copy_initiator_creds(OM_uint32 * minor_status,gss_cred_id_t input_cred_handle,const gss_OID desired_mech,OM_uint32 overwrite_cred,OM_uint32 default_cred,gss_const_key_value_set_t cred_store)61 copy_initiator_creds(OM_uint32 *minor_status,
62                      gss_cred_id_t input_cred_handle,
63                      const gss_OID desired_mech,
64                      OM_uint32 overwrite_cred,
65                      OM_uint32 default_cred,
66                      gss_const_key_value_set_t cred_store)
67 {
68     OM_uint32 major_status;
69     krb5_error_code code;
70     krb5_gss_cred_id_t kcred = NULL;
71     krb5_context context = NULL;
72     krb5_ccache ccache = NULL;
73     const char *ccache_name;
74 
75     *minor_status = 0;
76 
77     if (!default_cred && cred_store == GSS_C_NO_CRED_STORE) {
78         *minor_status = G_STORE_NON_DEFAULT_CRED_NOSUPP;
79         major_status = GSS_S_FAILURE;
80         goto cleanup;
81     }
82 
83     code = krb5_gss_init_context(&context);
84     if (code != 0) {
85         *minor_status = code;
86         major_status = GSS_S_FAILURE;
87         goto cleanup;
88     }
89 
90     major_status = krb5_gss_validate_cred_1(minor_status,
91                                             input_cred_handle,
92                                             context);
93     if (GSS_ERROR(major_status))
94         goto cleanup;
95 
96     kcred = (krb5_gss_cred_id_t)input_cred_handle;
97 
98     if (kcred->ccache == NULL) {
99         *minor_status = KG_CCACHE_NOMATCH;
100         major_status = GSS_S_DEFECTIVE_CREDENTIAL;
101         goto cleanup;
102     }
103 
104     if (!overwrite_cred &&
105         has_unexpired_creds(kcred, desired_mech, default_cred, cred_store)) {
106         major_status = GSS_S_DUPLICATE_ELEMENT;
107         goto cleanup;
108     }
109 
110     major_status = kg_value_from_cred_store(cred_store,
111                                             KRB5_CS_CCACHE_URN, &ccache_name);
112     if (GSS_ERROR(major_status))
113         goto cleanup;
114 
115     if (ccache_name != NULL) {
116         code = krb5_cc_resolve(context, ccache_name, &ccache);
117         if (code != 0) {
118             *minor_status = code;
119             major_status = GSS_S_FAILURE;
120             goto cleanup;
121         }
122         code = krb5_cc_initialize(context, ccache,
123                                   kcred->name->princ);
124         if (code != 0) {
125             *minor_status = code;
126             major_status = GSS_S_FAILURE;
127             goto cleanup;
128         }
129     }
130 
131     if (ccache == NULL) {
132         if (!default_cred) {
133             *minor_status = G_STORE_NON_DEFAULT_CRED_NOSUPP;
134             major_status = GSS_S_FAILURE;
135             goto cleanup;
136         }
137         code = krb5int_cc_default(context, &ccache);
138         if (code != 0) {
139             *minor_status = code;
140             major_status = GSS_S_FAILURE;
141             goto cleanup;
142         }
143     }
144 
145     code = krb5_cc_copy_creds(context, kcred->ccache, ccache);
146     if (code != 0) {
147         *minor_status = code;
148         major_status = GSS_S_FAILURE;
149         goto cleanup;
150     }
151 
152     *minor_status = 0;
153     major_status = GSS_S_COMPLETE;
154 
155 cleanup:
156     if (kcred != NULL)
157         k5_mutex_unlock(&kcred->lock);
158     if (ccache != NULL)
159         krb5_cc_close(context, ccache);
160     krb5_free_context(context);
161 
162     return major_status;
163 }
164 
165 OM_uint32 KRB5_CALLCONV
krb5_gss_store_cred(OM_uint32 * minor_status,gss_cred_id_t input_cred_handle,gss_cred_usage_t cred_usage,const gss_OID desired_mech,OM_uint32 overwrite_cred,OM_uint32 default_cred,gss_OID_set * elements_stored,gss_cred_usage_t * cred_usage_stored)166 krb5_gss_store_cred(OM_uint32 *minor_status,
167                     gss_cred_id_t input_cred_handle,
168                     gss_cred_usage_t cred_usage,
169                     const gss_OID desired_mech,
170                     OM_uint32 overwrite_cred,
171                     OM_uint32 default_cred,
172                     gss_OID_set *elements_stored,
173                     gss_cred_usage_t *cred_usage_stored)
174 {
175     return krb5_gss_store_cred_into(minor_status, input_cred_handle,
176                                     cred_usage, desired_mech,
177                                     overwrite_cred, default_cred,
178                                     GSS_C_NO_CRED_STORE,
179                                     elements_stored, cred_usage_stored);
180 }
181 
182 OM_uint32 KRB5_CALLCONV
krb5_gss_store_cred_into(OM_uint32 * minor_status,gss_cred_id_t input_cred_handle,gss_cred_usage_t cred_usage,const gss_OID desired_mech,OM_uint32 overwrite_cred,OM_uint32 default_cred,gss_const_key_value_set_t cred_store,gss_OID_set * elements_stored,gss_cred_usage_t * cred_usage_stored)183 krb5_gss_store_cred_into(OM_uint32 *minor_status,
184                          gss_cred_id_t input_cred_handle,
185                          gss_cred_usage_t cred_usage,
186                          const gss_OID desired_mech,
187                          OM_uint32 overwrite_cred,
188                          OM_uint32 default_cred,
189                          gss_const_key_value_set_t cred_store,
190                          gss_OID_set *elements_stored,
191                          gss_cred_usage_t *cred_usage_stored)
192 {
193     OM_uint32 major_status;
194     gss_cred_usage_t actual_usage;
195     OM_uint32 lifetime;
196 
197     if (input_cred_handle == GSS_C_NO_CREDENTIAL)
198         return GSS_S_NO_CRED;
199 
200     major_status = GSS_S_FAILURE;
201 
202     if (cred_usage == GSS_C_ACCEPT) {
203         *minor_status = G_STORE_ACCEPTOR_CRED_NOSUPP;
204         return GSS_S_FAILURE;
205     } else if (cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) {
206         *minor_status = G_BAD_USAGE;
207         return GSS_S_FAILURE;
208     }
209 
210     major_status = krb5_gss_inquire_cred(minor_status, input_cred_handle,
211                                          NULL, &lifetime,
212                                          &actual_usage, elements_stored);
213     if (GSS_ERROR(major_status))
214         return major_status;
215 
216     if (lifetime == 0)
217         return GSS_S_CREDENTIALS_EXPIRED;
218 
219     if (actual_usage != GSS_C_INITIATE && actual_usage != GSS_C_BOTH) {
220         *minor_status = G_STORE_ACCEPTOR_CRED_NOSUPP;
221         return GSS_S_FAILURE;
222     }
223 
224     major_status = copy_initiator_creds(minor_status, input_cred_handle,
225                                         desired_mech, overwrite_cred,
226                                         default_cred, cred_store);
227     if (GSS_ERROR(major_status))
228         return major_status;
229 
230     if (cred_usage_stored != NULL)
231         *cred_usage_stored = GSS_C_INITIATE;
232 
233     return GSS_S_COMPLETE;
234 }
235