1/*
2 * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "gssdigest.h"
35
36static int
37calculate(void *ptr,
38	  heim_scram_method method,
39	  unsigned int iterations,
40	  heim_scram_data *salt,
41	  const heim_scram_data *c1,
42	  const heim_scram_data *s1,
43	  const heim_scram_data *c2noproof,
44	  heim_scram_data *proof,
45	  heim_scram_data *server,
46	  heim_scram_data *sessionKey)
47{
48    krb5_error_code ret;
49    krb5_context context;
50    krb5_storage *request, *response = NULL;
51    krb5_data response_data;
52    scram_id_t ctx = ptr;
53
54    ret = krb5_init_context(&context);
55    if (ret) {
56	return ret;
57    }
58    ret = krb5_kcm_storage_request(context, KCM_OP_DO_SCRAM_AUTH, &request);
59    if (ret) {
60	krb5_free_context(context);
61	return GSS_S_FAILURE;
62    }
63    ret = krb5_store_stringz(request, ctx->client);
64    if (ret == 0)
65	ret = krb5_store_uint32(request, iterations);
66    if (ret == 0)
67	ret = krb5_store_data(request, *salt);
68    if (ret == 0)
69	ret = krb5_store_data(request, *c1);
70    if (ret == 0)
71	ret = krb5_store_data(request, *s1);
72    if (ret == 0)
73	ret = krb5_store_data(request, *c2noproof);
74    if (ret == 0)
75	ret = krb5_kcm_call(context, request, &response, &response_data);
76
77    krb5_free_context(context);
78    krb5_storage_free(request);
79    if (ret == 0)
80    ret = krb5_ret_data(response, proof);
81    if (ret == 0)
82	ret = krb5_ret_data(response, server);
83    if (ret == 0)
84	ret = krb5_ret_data(response, sessionKey);
85    if (ret)
86	ret = KRB5_CC_IO;
87
88    if (response)
89	krb5_storage_free(response);
90    krb5_data_free(&response_data);
91
92    return ret;
93
94}
95
96static struct heim_scram_client scram_client = {
97	SCRAM_CLIENT_VERSION_1,
98	calculate
99};
100
101OM_uint32
102_gss_scram_init_sec_context(OM_uint32 * minor_status,
103			    const gss_cred_id_t initiator_cred_handle,
104			    gss_ctx_id_t * context_handle,
105			    const gss_name_t target_name,
106			    const gss_OID mech_type,
107			    OM_uint32 req_flags,
108			    OM_uint32 time_req,
109			    const gss_channel_bindings_t input_chan_bindings,
110			    const gss_buffer_t input_token,
111			    gss_OID * actual_mech_type,
112			    gss_buffer_t output_token,
113			    OM_uint32 * ret_flags,
114			    OM_uint32 * time_rec)
115{
116    OM_uint32 major;
117    heim_scram_data in, out;
118    scram_id_t ctx;
119    int ret;
120
121    *minor_status = 0;
122
123    if (ret_flags)
124	*ret_flags = 0;
125    if (time_rec)
126	*time_rec = 0;
127    if (actual_mech_type)
128	*actual_mech_type = GSS_C_NO_OID;
129
130    ctx = (scram_id_t) *context_handle;
131
132    if (actual_mech_type)
133	*actual_mech_type = GSS_SCRAM_MECHANISM;
134    if (ret_flags)
135	*ret_flags = 0;
136
137    if (ctx == NULL) {
138        gss_buffer_desc outraw;
139
140        if (initiator_cred_handle == GSS_C_NO_CREDENTIAL)
141	    return GSS_S_FAILURE;
142
143	major = _gss_scram_have_cred(minor_status, (char *)initiator_cred_handle, NULL);
144	if (major != GSS_S_COMPLETE)
145	    return major;
146
147	ctx = calloc(1, sizeof(*ctx));
148	if (ctx == NULL) {
149	    *minor_status = ENOMEM;
150	    return gss_mg_set_error_string(GSS_SCRAM_MECHANISM, GSS_S_FAILURE,
151					   ENOMEM, "out of memory");
152	}
153	*context_handle = (gss_ctx_id_t) ctx;
154
155        ctx->client = strdup((char *)initiator_cred_handle);
156
157	ret = heim_scram_client1(ctx->client, NULL, HEIM_SCRAM_DIGEST_SHA1,
158				 &ctx->scram, &out);
159	if (ret) {
160	  _gss_scram_delete_sec_context(minor_status, context_handle, NULL);
161	  *minor_status = ret;
162	  return GSS_S_FAILURE;
163	}
164
165	outraw.value = out.data;
166	outraw.length = out.length;
167
168	major = gss_encapsulate_token(&outraw,
169				      GSS_SCRAM_MECHANISM,
170				      output_token);
171	if (major) {
172	    _gss_scram_delete_sec_context(minor_status, context_handle, NULL);
173	    *minor_status = ENOMEM;
174	    return GSS_S_FAILURE;
175	}
176
177	ctx->flags = req_flags;
178
179	_gss_mg_log(1, "scram-isc-client1: %s",
180		    ctx->client);
181
182	ctx->state = CLIENT2;
183
184	major = GSS_S_CONTINUE_NEEDED;
185
186    } else if (ctx->state == CLIENT2) {
187
188	in.data = input_token->value;
189	in.length = input_token->length;
190
191	ret = heim_scram_client2(&in, &scram_client, ctx, ctx->scram, &out);
192	if (ret) {
193	    _gss_scram_delete_sec_context(minor_status, context_handle, NULL);
194	    *minor_status = ret;
195	    return gss_mg_set_error_string(GSS_SCRAM_MECHANISM, GSS_S_FAILURE, ret,
196					   "failed to create scram response");
197	}
198
199	output_token->length = out.length;
200	output_token->value = malloc(out.length);
201
202	memcpy(output_token->value, out.data, out.length);
203
204	ctx->state = CLIENT3;
205
206	/*
207	 * Here we have session key, pull it out and set prot ready
208	 */
209
210	major = GSS_S_CONTINUE_NEEDED;
211
212    } else if (ctx->state == CLIENT3) {
213
214	in.data = input_token->value;
215	in.length = input_token->length;
216
217	ret = heim_scram_client3(&in, ctx->scram);
218	if (ret) {
219	    _gss_scram_delete_sec_context(minor_status, context_handle, NULL);
220	    *minor_status = ret;
221	    return gss_mg_set_error_string(GSS_SCRAM_MECHANISM, GSS_S_FAILURE, ret,
222					   "failed to verify server response");
223	}
224
225	output_token->length = 0;
226	output_token->value = NULL;
227
228	ctx->status |= STATUS_OPEN;
229
230	major = GSS_S_COMPLETE;
231
232    } else {
233	abort();
234    }
235
236    if (ret_flags)
237	*ret_flags = ctx->flags;
238
239    if (time_rec)
240	*time_rec = GSS_C_INDEFINITE;
241
242    return major;
243}
244