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