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