1/*
2 * Copyright (c) 2004, PADL Software Pty Ltd.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * 3. Neither the name of PADL Software nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include "gsskrb5_locl.h"
34#include <hex.h>
35#include <rtbl.h>
36
37static char*
38printable_time(time_t t)
39{
40    static char s[128];
41    char *p;
42
43    if ((p = ctime(&t)) == NULL)
44	strlcpy(s, "?", sizeof(s));
45    else
46	strlcpy(s, p + 4, sizeof(s));
47    s[20] = 0;
48    return s;
49}
50
51
52OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_cred_by_oid
53	   (OM_uint32 * minor_status,
54	    const gss_cred_id_t cred_handle,
55	    const gss_OID desired_object,
56	    gss_buffer_set_t *data_set)
57{
58    krb5_context context;
59    gsskrb5_cred cred = (gsskrb5_cred)cred_handle;
60    krb5_error_code ret;
61    gss_buffer_desc buffer;
62
63    GSSAPI_KRB5_INIT (&context);
64
65    if (gss_oid_equal(desired_object, GSS_KRB5_COPY_CCACHE_X)) {
66	char *str;
67
68	HEIMDAL_MUTEX_lock(&cred->cred_id_mutex);
69
70	if (cred->ccache == NULL) {
71	    HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
72	    *minor_status = EINVAL;
73	    return GSS_S_FAILURE;
74	}
75
76	ret = krb5_cc_get_full_name(context, cred->ccache, &str);
77	HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
78	if (ret) {
79	    *minor_status = ret;
80	    return GSS_S_FAILURE;
81	}
82
83	buffer.value = str;
84	buffer.length = strlen(str);
85
86	ret = gss_add_buffer_set_member(minor_status, &buffer, data_set);
87	if (ret != GSS_S_COMPLETE)
88	    _gsskrb5_clear_status ();
89
90	free(str);
91
92	*minor_status = 0;
93	return GSS_S_COMPLETE;
94
95    } else if (gss_oid_equal(desired_object, GSS_C_CRED_VALIDATE)) {
96	krb5_verify_init_creds_opt vopt;
97	krb5_creds *kcred = NULL;
98
99	if (cred->ccache == NULL || cred->principal == NULL)
100	    return GSS_S_FAILURE;
101
102	krb5_verify_init_creds_opt_init(&vopt);
103	krb5_verify_init_creds_opt_set_ap_req_nofail(&vopt, TRUE);
104
105	HEIMDAL_MUTEX_lock(&cred->cred_id_mutex);
106
107	ret = _krb5_get_krbtgt(context, cred->ccache, cred->principal->realm, &kcred);
108	if (ret == 0) {
109	    ret = krb5_verify_init_creds(context,
110					 kcred,
111					 NULL,
112					 NULL,
113					 &cred->ccache,
114					 &vopt);
115	    krb5_free_creds(context, kcred);
116	}
117	HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
118
119	if (ret) {
120	    *minor_status = ret;
121	    return GSS_S_FAILURE;
122	}
123
124	return GSS_S_COMPLETE;
125
126    } else if (gss_oid_equal(desired_object, GSS_C_NT_UUID)) {
127	krb5_uuid uuid;
128	char *str;
129
130	HEIMDAL_MUTEX_lock(&cred->cred_id_mutex);
131
132	if (cred->ccache == NULL) {
133	    HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
134	    *minor_status = EINVAL;
135	    return GSS_S_FAILURE;
136	}
137
138	ret = krb5_cc_get_uuid(context, cred->ccache, uuid);
139	HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
140	if (ret) {
141	    *minor_status = ret;
142	    return GSS_S_FAILURE;
143	}
144
145	str = krb5_uuid_to_string(uuid);
146	if (str == NULL) {
147	    *minor_status = ENOMEM;
148	    return GSS_S_FAILURE;
149	}
150
151	buffer.value = str;
152	buffer.length = strlen(str);
153
154	ret = gss_add_buffer_set_member(minor_status, &buffer, data_set);
155	free(str);
156	if (ret != GSS_S_COMPLETE)
157	    _gsskrb5_clear_status ();
158
159	return GSS_S_COMPLETE;
160
161    } else if (gss_oid_equal(desired_object, GSS_C_CRED_DIAG)) {
162	krb5_cc_cursor cursor;
163	krb5_creds creds;
164#ifdef HAVE_KCM
165	krb5_data data;
166#endif
167	rtbl_t ct = NULL;
168	char *str = NULL;
169
170	if (cred->ccache == NULL) {
171	    *minor_status = EINVAL;
172	    return GSS_S_FAILURE;
173	}
174
175	/*
176	 * Cache name
177	 */
178
179	ret = krb5_cc_get_full_name(context, cred->ccache, &str);
180	HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
181	if (ret) {
182	    *minor_status = ret;
183	    return GSS_S_FAILURE;
184	}
185
186	buffer.value = str;
187	buffer.length = strlen(str);
188
189	ret = gss_add_buffer_set_member(minor_status, &buffer, data_set);
190	free(str);
191	if (ret != GSS_S_COMPLETE)
192	    _gsskrb5_clear_status ();
193
194	/*
195	 * cache list
196	 */
197
198	ct = rtbl_create();
199	if (ct == NULL) {
200	    *minor_status = ENOMEM;
201	    return GSS_S_FAILURE;
202	}
203
204#define COL_ISSUED "Issued"
205#define COL_EXPIRES "Expires"
206#define COL_PRINCIPAL "Principal"
207#define COL_ENCTYPE "Enctype"
208
209	rtbl_add_column(ct, COL_PRINCIPAL, 0);
210	rtbl_add_column(ct, COL_ISSUED, 0);
211	rtbl_add_column(ct, COL_EXPIRES, 0);
212	rtbl_add_column(ct, COL_ENCTYPE, 0);
213	rtbl_set_separator(ct, "  ");
214
215	HEIMDAL_MUTEX_lock(&cred->cred_id_mutex);
216
217	ret = krb5_cc_start_seq_get (context, cred->ccache, &cursor);
218	if (ret)
219	    goto out;
220
221	while ((ret = krb5_cc_next_cred (context, cred->ccache, &cursor, &creds)) == 0) {
222
223	    ret = krb5_unparse_name(context, creds.server, &str);
224	    if (ret)
225		goto next;
226
227	    rtbl_add_column_entry(ct, COL_PRINCIPAL, str);
228	    free(str);
229
230	    rtbl_add_column_entry(ct, COL_ISSUED,
231				  printable_time(creds.times.starttime));
232	    if (time(NULL) < creds.times.endtime)
233		rtbl_add_column_entry(ct, COL_EXPIRES,
234				      printable_time(creds.times.endtime));
235	    else
236		rtbl_add_column_entry(ct, COL_EXPIRES, "Expired");
237
238	    ret = krb5_enctype_to_string(context, creds.session.keytype, &str);
239	    if (ret)
240		goto next;
241	    rtbl_add_column_entry(ct, COL_ENCTYPE, str);
242	    free(str);
243
244	next:
245	    krb5_free_cred_contents (context, &creds);
246	}
247	(void)krb5_cc_end_seq_get (context, cred->ccache, &cursor);
248
249	if (ret != KRB5_CC_END)
250	    goto out;
251
252
253	buffer.value = rtbl_format_str(ct);
254	if (buffer.value == NULL) {
255	    *minor_status = ENOMEM;
256	    return GSS_S_FAILURE;
257	}
258	buffer.length = strlen((char *)buffer.value);
259
260	ret = gss_add_buffer_set_member(minor_status, &buffer, data_set);
261	free(buffer.value);
262	if (ret != GSS_S_COMPLETE)
263	    _gsskrb5_clear_status ();
264
265
266#ifdef HAVE_KCM
267	/*
268	 * kcm status
269	 */
270
271	ret = krb5_cc_get_config(context, cred->ccache, NULL, "kcm-status", &data);
272	if (ret == 0) {
273	    uint32_t num;
274	    int status = -1, kcmret = -1;
275	    if (data.length >= 8) {
276		memcpy(&num, ((int8_t*)data.data) + 4, sizeof(num));
277		status = (int)ntohl(num);
278	    }
279	    if (data.length >= 12) {
280		memcpy(&num, ((int8_t*)data.data) + 8, sizeof(num));
281		kcmret = (int)ntohl(num);
282	    }
283	    ret = asprintf(&str, "kcm-status: %s ret: %d",
284			   _krb5_kcm_get_status(status), kcmret);
285	    krb5_data_free(&data);
286	    if (ret < 0)
287		goto out;
288	    buffer.value = str;
289	    buffer.length = strlen(str);
290
291	    ret = gss_add_buffer_set_member(minor_status, &buffer, data_set);
292	    free(str);
293	    if (ret != GSS_S_COMPLETE)
294		_gsskrb5_clear_status ();
295	}
296#else
297	buffer.value = NULL;
298	buffer.length = 0;
299	(void)gss_add_buffer_set_member(minor_status, &buffer, data_set);
300#endif
301	ret = 0;
302
303    out:
304	HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
305
306	if (ct)
307	    rtbl_destroy(ct);
308
309	if (ret) {
310	    *minor_status = ret;
311	    return GSS_S_FAILURE;
312	}
313
314	return GSS_S_COMPLETE;
315
316    } else if (gss_oid_equal(desired_object, GSS_C_CRED_SET_DEFAULT)) {
317
318	HEIMDAL_MUTEX_lock(&cred->cred_id_mutex);
319
320	if (cred->ccache)
321	    ret = krb5_cc_switch(context, cred->ccache);
322	else
323	    ret = EINVAL;
324
325	HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
326
327	if (ret) {
328	    *minor_status = ret;
329	    return GSS_S_FAILURE;
330	}
331
332	buffer.value = (void *)"default";
333	buffer.length = 7;
334	return gss_add_buffer_set_member(minor_status, &buffer, data_set);
335
336    } else if (gss_oid_equal(desired_object, GSS_C_CRED_GET_DEFAULT)) {
337	const char *defname;
338	char *fn = NULL;
339
340	HEIMDAL_MUTEX_lock(&cred->cred_id_mutex);
341	ret = krb5_cc_get_full_name(context, cred->ccache, &fn);
342	HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
343
344	if (ret) {
345	    *minor_status = ret;
346	    return GSS_S_FAILURE;
347	}
348
349	defname = krb5_cc_default_name(context);
350	if (defname && strcmp(fn, defname) == 0)
351	    ret = 0;
352	else
353	    ret = EINVAL;
354	free(fn);
355
356	if (ret) {
357	    *minor_status = ret;
358	    return GSS_S_FAILURE;
359	}
360
361	buffer.value = (void *)"default";
362	buffer.length = 7;
363	return gss_add_buffer_set_member(minor_status, &buffer, data_set);
364
365    } else if (gss_oid_equal(desired_object, GSS_C_CRED_RENEW)) {
366	krb5_creds in, *out = NULL;
367	krb5_kdc_flags flags;
368	const char *realm;
369
370	memset(&in, 0, sizeof(in));
371
372	HEIMDAL_MUTEX_lock(&cred->cred_id_mutex);
373
374	ret = krb5_cc_get_principal(context, cred->ccache, &in.client);
375	if(ret) {
376	    HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
377	    *minor_status = ret;
378	    return GSS_S_FAILURE;
379	}
380	realm = krb5_principal_get_realm(context, in.client);
381	ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME, realm, NULL);
382	if(ret) {
383	    HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
384	    krb5_free_cred_contents(context, &in);
385	    *minor_status = ret;
386	    return GSS_S_FAILURE;
387	}
388
389	ret = krb5_get_credentials(context, KRB5_GC_CACHED, cred->ccache, &in, &out);
390	if (ret) {
391	    HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
392	    krb5_free_cred_contents(context, &in);
393	    *minor_status = ret;
394	    return GSS_S_FAILURE;
395	}
396
397	if (out->flags.b.renewable == 0) {
398	    HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
399	    krb5_free_cred_contents(context, &in);
400	    krb5_free_creds(context, out);
401	    krb5_set_error_message(context, GSS_KRB5_S_G_BAD_USAGE, "Credential is not renewable");
402	    *minor_status = GSS_KRB5_S_G_BAD_USAGE;
403	    return GSS_S_FAILURE;
404	}
405
406	flags.i = 0;
407	flags.b.renewable         = out->flags.b.renewable;
408	flags.b.forwardable       = out->flags.b.forwardable;
409	flags.b.proxiable         = out->flags.b.proxiable;
410
411	krb5_free_creds(context, out);
412	out = NULL;
413
414	ret = krb5_get_kdc_cred(context,
415				cred->ccache,
416				flags,
417				NULL,
418				NULL,
419				&in,
420				&out);
421	if(ret == 0) {
422	    (void)krb5_cc_remove_cred(context, cred->ccache, 0, &in);
423	    ret = krb5_cc_store_cred(context, cred->ccache, out);
424	    krb5_free_creds (context, out);
425	}
426	krb5_free_cred_contents(context, &in);
427	HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
428
429	if (ret) {
430	    *minor_status = ret;
431	    return GSS_S_FAILURE;
432	}
433
434	buffer.value = (void *)"renewed";
435	buffer.length = 7;
436	return gss_add_buffer_set_member(minor_status, &buffer, data_set);
437
438    } else {
439	*minor_status = EINVAL;
440	return GSS_S_FAILURE;
441    }
442}
443
444