1 /*
2 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
3 */
4 /*
5 * Copyright 2000 by the Massachusetts Institute of Technology.
6 * All Rights Reserved.
7 *
8 * Export of this software from the United States of America may
9 * require a specific license from the United States Government.
10 * It is the responsibility of any person or organization contemplating
11 * export to obtain such a license before exporting.
12 *
13 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14 * distribute this software and its documentation for any purpose and
15 * without fee is hereby granted, provided that the above copyright
16 * notice appear in all copies and that both that copyright notice and
17 * this permission notice appear in supporting documentation, and that
18 * the name of M.I.T. not be used in advertising or publicity pertaining
19 * to distribution of the software without specific, written prior
20 * permission. Furthermore if you modify this software you must label
21 * your software as modified software and not distribute it in such a
22 * fashion that it might be confused with the original M.I.T. software.
23 * M.I.T. makes no representations about the suitability of
24 * this software for any purpose. It is provided "as is" without express
25 * or implied warranty.
26 *
27 */
28 /*
29 * Copyright (C) 1998 by the FundsXpress, INC.
30 *
31 * All rights reserved.
32 *
33 * Export of this software from the United States of America may require
34 * a specific license from the United States Government. It is the
35 * responsibility of any person or organization contemplating export to
36 * obtain such a license before exporting.
37 *
38 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
39 * distribute this software and its documentation for any purpose and
40 * without fee is hereby granted, provided that the above copyright
41 * notice appear in all copies and that both that copyright notice and
42 * this permission notice appear in supporting documentation, and that
43 * the name of FundsXpress. not be used in advertising or publicity pertaining
44 * to distribution of the software without specific, written prior
45 * permission. FundsXpress makes no representations about the suitability of
46 * this software for any purpose. It is provided "as is" without express
47 * or implied warranty.
48 *
49 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
50 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
51 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
52 */
53
54 #include "gssapiP_krb5.h"
55 #ifdef HAVE_STRING_H
56 #include <string.h>
57 #else
58 #include <strings.h>
59 #endif
60 #include <locale.h> /* Solaris Kerberos */
61
62 /*
63 * $Id: add_cred.c 18396 2006-07-25 20:29:43Z lxs $
64 */
65
66 /* V2 interface */
67 OM_uint32
krb5_gss_add_cred(minor_status,input_cred_handle,desired_name,desired_mech,cred_usage,initiator_time_req,acceptor_time_req,output_cred_handle,actual_mechs,initiator_time_rec,acceptor_time_rec)68 krb5_gss_add_cred(minor_status, input_cred_handle,
69 desired_name, desired_mech, cred_usage,
70 initiator_time_req, acceptor_time_req,
71 output_cred_handle, actual_mechs,
72 initiator_time_rec, acceptor_time_rec)
73 OM_uint32 *minor_status;
74 gss_cred_id_t input_cred_handle;
75 gss_name_t desired_name;
76 gss_OID desired_mech;
77 gss_cred_usage_t cred_usage;
78 OM_uint32 initiator_time_req;
79 OM_uint32 acceptor_time_req;
80 gss_cred_id_t *output_cred_handle;
81 gss_OID_set *actual_mechs;
82 OM_uint32 *initiator_time_rec;
83 OM_uint32 *acceptor_time_rec;
84 {
85 krb5_context context;
86 OM_uint32 major_status, lifetime;
87 krb5_gss_cred_id_t cred;
88 krb5_error_code code;
89
90 /* this is pretty simple, since there's not really any difference
91 between the underlying mechanisms. The main hair is in copying
92 a mechanism if requested. */
93
94 /* check if the desired_mech is bogus */
95
96 if (!g_OID_equal(desired_mech, gss_mech_krb5) &&
97 !g_OID_equal(desired_mech, gss_mech_krb5_old)) {
98 *minor_status = 0;
99 return(GSS_S_BAD_MECH);
100 }
101
102 /* check if the desired_mech is bogus */
103
104 if ((cred_usage != GSS_C_INITIATE) &&
105 (cred_usage != GSS_C_ACCEPT) &&
106 (cred_usage != GSS_C_BOTH)) {
107 *minor_status = (OM_uint32) G_BAD_USAGE;
108 return(GSS_S_FAILURE);
109 }
110
111 /* since the default credential includes all the mechanisms,
112 return an error for that case. */
113
114 /*SUPPRESS 29*/
115 if (input_cred_handle == GSS_C_NO_CREDENTIAL) {
116 *minor_status = 0;
117 return(GSS_S_DUPLICATE_ELEMENT);
118 }
119
120 code = krb5_gss_init_context(&context);
121 if (code) {
122 *minor_status = code;
123 return GSS_S_FAILURE;
124 }
125
126 major_status = krb5_gss_validate_cred_1(minor_status, input_cred_handle,
127 context);
128 if (GSS_ERROR(major_status)) {
129 save_error_info(*minor_status, context);
130 krb5_free_context(context);
131 return major_status;
132 }
133
134 cred = (krb5_gss_cred_id_t) input_cred_handle;
135 k5_mutex_assert_locked(&cred->lock);
136
137 /* check if the cred_usage is equal or "less" than the passed-in cred
138 if copying */
139
140 if (!((cred->usage == cred_usage) ||
141 ((cred->usage == GSS_C_BOTH) &&
142 (output_cred_handle != NULL)))) {
143 *minor_status = (OM_uint32) G_BAD_USAGE;
144 krb5_free_context(context);
145 return(GSS_S_FAILURE);
146 }
147
148 /* check that desired_mech isn't already in the credential */
149
150 if ((g_OID_equal(desired_mech, gss_mech_krb5_old) && cred->prerfc_mech) ||
151 (g_OID_equal(desired_mech, gss_mech_krb5) && cred->rfc_mech)) {
152 *minor_status = 0;
153 krb5_free_context(context);
154 return(GSS_S_DUPLICATE_ELEMENT);
155 }
156
157 if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) {
158 save_error_info(*minor_status, context);
159 krb5_free_context(context);
160 return GSS_S_FAILURE;
161 }
162
163 /* verify the desired_name */
164
165 /*SUPPRESS 29*/
166 if ((desired_name != (gss_name_t) NULL) &&
167 (! kg_validate_name(desired_name))) {
168 *minor_status = (OM_uint32) G_VALIDATE_FAILED;
169 krb5_free_context(context);
170 return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
171 }
172
173 /* make sure the desired_name is the same as the existing one */
174
175 if (desired_name &&
176 !krb5_principal_compare(context, (krb5_principal) desired_name,
177 cred->princ)) {
178 /* Solaris Kerberos: spruce-up the err msg */
179 krb5_principal dname = (krb5_principal) desired_name;
180 char *s_name = NULL, *s_princ= NULL;
181 int kret = krb5_unparse_name(context, dname, &s_name);
182 int kret1 = krb5_unparse_name(context, cred->princ, &s_princ);
183 *minor_status = (OM_uint32) G_BAD_USAGE;
184 if (kret == 0 && kret1 == 0) {
185 krb5_set_error_message(context, *minor_status,
186 dgettext(TEXT_DOMAIN,
187 "Desired name principal '%s' does not match '%s'"),
188 s_name, s_princ);
189 save_error_info(*minor_status, context);
190 }
191 if (s_name)
192 krb5_free_unparsed_name(context, s_name);
193 if (s_princ)
194 krb5_free_unparsed_name(context, s_princ);
195
196 krb5_free_context(context);
197 return(GSS_S_BAD_NAME);
198 }
199
200 /* copy the cred if necessary */
201
202 if (output_cred_handle) {
203 /* make a copy */
204 krb5_gss_cred_id_t new_cred;
205 char *kttype, ktboth[1024];
206 const char *cctype, *ccname;
207 char ccboth[1024];
208
209 if ((new_cred =
210 (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec)))
211 == NULL) {
212 *minor_status = ENOMEM;
213 krb5_free_context(context);
214 return(GSS_S_FAILURE);
215 }
216 memset(new_cred, 0, sizeof(krb5_gss_cred_id_rec));
217
218 new_cred->usage = cred_usage;
219 new_cred->prerfc_mech = cred->prerfc_mech;
220 new_cred->rfc_mech = cred->rfc_mech;
221 new_cred->tgt_expire = cred->tgt_expire;
222
223 if (cred->princ)
224 code = krb5_copy_principal(context, cred->princ, &new_cred->princ);
225 if (code) {
226 xfree(new_cred);
227
228 *minor_status = code;
229 save_error_info(*minor_status, context);
230 krb5_free_context(context);
231 return(GSS_S_FAILURE);
232 }
233
234 if (cred->keytab) {
235 kttype = krb5_kt_get_type(context, cred->keytab);
236 if ((strlen(kttype)+2) > sizeof(ktboth)) {
237 if (new_cred->princ)
238 krb5_free_principal(context, new_cred->princ);
239 xfree(new_cred);
240
241 *minor_status = ENOMEM;
242 krb5_free_context(context);
243 return(GSS_S_FAILURE);
244 }
245
246 strncpy(ktboth, kttype, sizeof(ktboth) - 1);
247 ktboth[sizeof(ktboth) - 1] = '\0';
248 strncat(ktboth, ":", sizeof(ktboth) - 1 - strlen(ktboth));
249
250 code = krb5_kt_get_name(context, cred->keytab,
251 ktboth+strlen(ktboth),
252 sizeof(ktboth)-strlen(ktboth));
253 if (code) {
254 if(new_cred->princ)
255 krb5_free_principal(context, new_cred->princ);
256 xfree(new_cred);
257
258 *minor_status = code;
259 save_error_info(*minor_status, context);
260 krb5_free_context(context);
261 return(GSS_S_FAILURE);
262 }
263
264 code = krb5_kt_resolve(context, ktboth, &new_cred->keytab);
265 if (code) {
266 if (new_cred->princ)
267 krb5_free_principal(context, new_cred->princ);
268 xfree(new_cred);
269
270 *minor_status = code;
271 save_error_info(*minor_status, context);
272 krb5_free_context(context);
273 return(GSS_S_FAILURE);
274 }
275 } else {
276 new_cred->keytab = NULL;
277 }
278
279 if (cred->rcache) {
280 /* Open the replay cache for this principal. */
281 if ((code = krb5_get_server_rcache(context,
282 krb5_princ_component(context, cred->princ, 0),
283 &new_cred->rcache))) {
284 if (new_cred->keytab)
285 krb5_kt_close(context, new_cred->keytab);
286 if (new_cred->princ)
287 krb5_free_principal(context, new_cred->princ);
288 xfree(new_cred);
289
290 *minor_status = code;
291 save_error_info(*minor_status, context);
292 krb5_free_context(context);
293 return(GSS_S_FAILURE);
294 }
295 } else {
296 new_cred->rcache = NULL;
297 }
298
299 if (cred->ccache) {
300 cctype = krb5_cc_get_type(context, cred->ccache);
301 ccname = krb5_cc_get_name(context, cred->ccache);
302
303 if ((strlen(cctype)+strlen(ccname)+2) > sizeof(ccboth)) {
304 if (new_cred->rcache)
305 krb5_rc_close(context, new_cred->rcache);
306 if (new_cred->keytab)
307 krb5_kt_close(context, new_cred->keytab);
308 if (new_cred->princ)
309 krb5_free_principal(context, new_cred->princ);
310 xfree(new_cred);
311
312 krb5_free_context(context);
313 *minor_status = ENOMEM;
314 return(GSS_S_FAILURE);
315 }
316
317 strncpy(ccboth, cctype, sizeof(ccboth) - 1);
318 ccboth[sizeof(ccboth) - 1] = '\0';
319 strncat(ccboth, ":", sizeof(ccboth) - 1 - strlen(ccboth));
320 strncat(ccboth, ccname, sizeof(ccboth) - 1 - strlen(ccboth));
321
322 code = krb5_cc_resolve(context, ccboth, &new_cred->ccache);
323 if (code) {
324 if (new_cred->rcache)
325 krb5_rc_close(context, new_cred->rcache);
326 if (new_cred->keytab)
327 krb5_kt_close(context, new_cred->keytab);
328 if (new_cred->princ)
329 krb5_free_principal(context, new_cred->princ);
330 xfree(new_cred);
331 *minor_status = code;
332 save_error_info(*minor_status, context);
333 krb5_free_context(context);
334 return(GSS_S_FAILURE);
335 }
336 } else {
337 new_cred->ccache = NULL;
338 }
339
340 /* intern the credential handle */
341
342 if (! kg_save_cred_id((gss_cred_id_t) new_cred)) {
343 if (new_cred->ccache)
344 krb5_cc_close(context, new_cred->ccache);
345 if (new_cred->rcache)
346 krb5_rc_close(context, new_cred->rcache);
347 if (new_cred->keytab)
348 krb5_kt_close(context, new_cred->keytab);
349 if (new_cred->princ)
350 krb5_free_principal(context, new_cred->princ);
351 xfree(new_cred);
352 krb5_free_context(context);
353
354 *minor_status = (OM_uint32) G_VALIDATE_FAILED;
355 return(GSS_S_FAILURE);
356 }
357
358 /* modify new_cred */
359
360 cred = new_cred;
361 }
362
363 /* set the flag for the new mechanism */
364
365 if (g_OID_equal(desired_mech, gss_mech_krb5_old))
366 cred->prerfc_mech = 1;
367 else if (g_OID_equal(desired_mech, gss_mech_krb5))
368 cred->rfc_mech = 1;
369
370 /* set the outputs */
371
372 if (GSS_ERROR(major_status = krb5_gss_inquire_cred(minor_status,
373 (gss_cred_id_t)cred,
374 NULL, &lifetime,
375 NULL, actual_mechs))) {
376 OM_uint32 dummy;
377
378 if (output_cred_handle)
379 (void) krb5_gss_release_cred(&dummy, (gss_cred_id_t *) &cred);
380 krb5_free_context(context);
381
382 return(major_status);
383 }
384
385 if (initiator_time_rec)
386 *initiator_time_rec = lifetime;
387 if (acceptor_time_rec)
388 *acceptor_time_rec = lifetime;
389
390 if (output_cred_handle)
391 *output_cred_handle = (gss_cred_id_t)cred;
392
393 krb5_free_context(context);
394 *minor_status = 0;
395 return(GSS_S_COMPLETE);
396 }
397