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