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