1/*
2 * Copyright (c) 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "ntlm.h"
37
38OM_uint32 _gss_ntlm_inquire_cred
39           (OM_uint32 * minor_status,
40            const gss_cred_id_t cred_handle,
41            gss_name_t * name,
42            OM_uint32 * lifetime,
43            gss_cred_usage_t * cred_usage,
44            gss_OID_set * mechanisms
45           )
46{
47    OM_uint32 ret, junk;
48
49    *minor_status = 0;
50
51    if (cred_handle == NULL)
52	return GSS_S_NO_CRED;
53
54    if (name) {
55	ret = _gss_ntlm_duplicate_name(minor_status,
56				       (gss_name_t)cred_handle,
57				       name);
58	if (ret)
59	    goto out;
60    }
61    if (lifetime)
62	*lifetime = GSS_C_INDEFINITE;
63    if (cred_usage)
64	*cred_usage = 0;
65    if (mechanisms)
66	*mechanisms = GSS_C_NO_OID_SET;
67
68    if (cred_handle == GSS_C_NO_CREDENTIAL)
69	return GSS_S_NO_CRED;
70
71    if (mechanisms) {
72        ret = gss_create_empty_oid_set(minor_status, mechanisms);
73        if (ret)
74	    goto out;
75	ret = gss_add_oid_set_member(minor_status,
76				     GSS_NTLM_MECHANISM,
77				     mechanisms);
78        if (ret)
79	    goto out;
80    }
81
82    return GSS_S_COMPLETE;
83out:
84    gss_release_oid_set(&junk, mechanisms);
85    return ret;
86}
87
88OM_uint32
89_gss_ntlm_destroy_cred(OM_uint32 *minor_status,
90		       gss_cred_id_t *cred_handle)
91{
92    krb5_error_code ret;
93    krb5_storage *request = NULL, *response = NULL;
94    krb5_data response_data;
95    krb5_context context;
96    ntlm_cred cred;
97    ssize_t sret;
98
99    if (cred_handle == NULL || *cred_handle == GSS_C_NO_CREDENTIAL)
100	return GSS_S_COMPLETE;
101
102    cred = (ntlm_cred)*cred_handle;
103
104    if ((cred->flags & NTLM_UUID) == 0)
105	return _gss_ntlm_release_cred(minor_status, cred_handle);
106
107    ret = krb5_init_context(&context);
108    if (ret) {
109	*minor_status = ret;
110	return GSS_S_FAILURE;
111    }
112
113    ret = krb5_kcm_storage_request(context, KCM_OP_DESTROY_CRED, &request);
114    if (ret) {
115	request = NULL;
116	goto out;
117    }
118
119    sret = krb5_storage_write(request, cred->uuid, sizeof(cred->uuid));
120    if (sret != sizeof(cred->uuid)) {
121	ret = KRB5_CC_IO;
122	goto out;
123    }
124
125    ret = krb5_kcm_call(context, request, &response, &response_data);
126
127 out:
128    if (request)
129        krb5_storage_free(request);
130    if (response) {
131        krb5_storage_free(response);
132	krb5_data_free(&response_data);
133    }
134
135    krb5_free_context(context);
136    if (ret) {
137	*minor_status = ret;
138	return GSS_S_FAILURE;
139    }
140
141    return _gss_ntlm_release_cred(minor_status, cred_handle);
142}
143
144static OM_uint32
145change_hold(OM_uint32 *minor_status, ntlm_cred cred, int op)
146{
147    krb5_storage *request = NULL, *response = NULL;
148    krb5_data response_data;
149    krb5_context context;
150    krb5_error_code ret;
151    ssize_t sret;
152
153    *minor_status = 0;
154
155    if (cred == NULL)
156	return GSS_S_COMPLETE;
157
158    if ((cred->flags & NTLM_UUID) == 0)
159	return GSS_S_COMPLETE;
160
161    ret = krb5_init_context(&context);
162    if (ret) {
163	*minor_status = ret;
164	return GSS_S_FAILURE;
165    }
166
167    ret = krb5_kcm_storage_request(context, op, &request);
168    if (ret) {
169	request = NULL;
170	goto out;
171    }
172
173    sret = krb5_storage_write(request, cred->uuid, sizeof(cred->uuid));
174    if (sret != sizeof(cred->uuid)) {
175	ret = KRB5_CC_IO;
176	goto out;
177    }
178
179    ret = krb5_kcm_call(context, request, &response, &response_data);
180    if (ret)
181	goto out;
182
183 out:
184    if (request)
185	krb5_storage_free(request);
186    if (response) {
187	krb5_storage_free(response);
188	krb5_data_free(&response_data);
189    }
190    krb5_free_context(context);
191    if (ret) {
192	*minor_status = ret;
193	return GSS_S_FAILURE;
194    }
195
196    return GSS_S_COMPLETE;
197}
198
199OM_uint32
200_gss_ntlm_cred_hold(OM_uint32 *minor_status, gss_cred_id_t cred)
201{
202    return change_hold(minor_status, (ntlm_cred)cred, KCM_OP_RETAIN_CRED);
203}
204
205OM_uint32
206_gss_ntlm_cred_unhold(OM_uint32 *minor_status, gss_cred_id_t cred)
207{
208    return change_hold(minor_status, (ntlm_cred)cred, KCM_OP_RELEASE_CRED);
209}
210
211OM_uint32
212_gss_ntlm_cred_label_get(OM_uint32 *minor_status, gss_cred_id_t cred_handle,
213			const char *label, gss_buffer_t value)
214{
215    ntlm_cred cred = (ntlm_cred)cred_handle;
216    krb5_storage *request = NULL, *response = NULL;
217    krb5_data response_data, data;
218    krb5_error_code ret;
219    krb5_context context;
220    ssize_t sret;
221
222    *minor_status = 0;
223
224    if (cred == NULL)
225	return GSS_S_COMPLETE;
226
227    if ((cred->flags & NTLM_UUID) == 0)
228	return GSS_S_COMPLETE;
229
230    ret = krb5_init_context(&context);
231    if (ret) {
232	*minor_status = ret;
233	return GSS_S_FAILURE;
234    }
235
236    ret = krb5_kcm_storage_request(context, KCM_OP_CRED_LABEL_GET, &request);
237    if (ret) {
238	request = NULL;
239	goto out;
240    }
241
242    sret = krb5_storage_write(request, cred->uuid, sizeof(cred->uuid));
243    if (sret != sizeof(cred->uuid)) {
244	ret = KRB5_CC_IO;
245	goto out;
246    }
247
248    ret = krb5_store_stringz(request, label);
249    if (ret)
250	goto out;
251
252    ret = krb5_kcm_call(context, request, &response, &response_data);
253    if (ret)
254	goto out;
255
256    ret = krb5_ret_data(response, &data);
257    if (ret)
258	goto out;
259
260    value->value = data.data;
261    value->length = data.length;
262
263 out:
264    if (request)
265	krb5_storage_free(request);
266    if (response) {
267	krb5_storage_free(response);
268	krb5_data_free(&response_data);
269    }
270    krb5_free_context(context);
271    if (ret) {
272	*minor_status = ret;
273	return GSS_S_FAILURE;
274    }
275
276    return GSS_S_COMPLETE;
277}
278
279OM_uint32
280_gss_ntlm_cred_label_set(OM_uint32 *minor_status, gss_cred_id_t cred_handle,
281			 const char *label, gss_buffer_t value)
282{
283    ntlm_cred cred = (ntlm_cred)cred_handle;
284    krb5_storage *request = NULL, *response = NULL;
285    krb5_data response_data;
286    krb5_context context;
287    krb5_error_code ret;
288    krb5_data data;
289    ssize_t sret;
290
291    *minor_status = 0;
292
293    if (cred == NULL)
294	return GSS_S_COMPLETE;
295
296    if ((cred->flags & NTLM_UUID) == 0)
297	return GSS_S_COMPLETE;
298
299    ret = krb5_init_context(&context);
300    if (ret) {
301	*minor_status = ret;
302	return GSS_S_FAILURE;
303    }
304
305    ret = krb5_kcm_storage_request(context, KCM_OP_CRED_LABEL_SET, &request);
306    if (ret) {
307	request = NULL;
308	goto out;
309    }
310
311    sret = krb5_storage_write(request, cred->uuid, sizeof(cred->uuid));
312    if (sret != sizeof(cred->uuid)) {
313	ret = KRB5_CC_IO;
314	goto out;
315    }
316
317    ret = krb5_store_stringz(request, label);
318    if (ret)
319	goto out;
320
321    if (value) {
322	data.data = value->value;
323	data.length = value->length;
324    } else {
325	krb5_data_zero(&data);
326    }
327
328    ret = krb5_store_data(request, data);
329    if (ret)
330	goto out;
331
332    ret = krb5_kcm_call(context, request, &response, &response_data);
333    if (ret)
334	goto out;
335
336 out:
337    if (request)
338	krb5_storage_free(request);
339    if (response) {
340	krb5_storage_free(response);
341	krb5_data_free(&response_data);
342    }
343
344    krb5_free_context(context);
345    if (ret) {
346	*minor_status = ret;
347	return GSS_S_FAILURE;
348    }
349
350    return GSS_S_COMPLETE;
351}
352