Add.
[gsasl.git] / lib / kerberos_v5 / client.c
bloba5681fd1c1a31c4f15f3299d48f28cd3f13a0c7b
1 /* client.c --- Experimental SASL mechanism KERBEROS_V5, client side.
2 * Copyright (C) 2003, 2004 Simon Josefsson
4 * This file is part of GNU SASL Library.
6 * GNU SASL Library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public License
8 * as published by the Free Software Foundation; either version 2.1 of
9 * the License, or (at your option) any later version.
11 * GNU SASL Library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with GNU SASL Library; if not, write to the Free
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
21 * NB! Shishi is licensed under GPL, so linking GSASL with it require
22 * that you follow the GPL for GSASL as well.
26 #include "kerberos_v5.h"
28 #include "shared.h"
30 struct _Gsasl_kerberos_v5_client_state
32 int step;
33 char serverhello[BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN];
34 int serverqops;
35 int clientqop;
36 int servermutual;
37 uint32_t servermaxbuf;
38 uint32_t clientmaxbuf;
39 Shishi *sh;
40 Shishi_tkt *tkt;
41 Shishi_as *as;
42 Shishi_ap *ap;
43 Shishi_key *sessionkey;
44 Shishi_safe *safe;
47 int
48 _gsasl_kerberos_v5_client_init (Gsasl_ctx * ctx)
50 if (!shishi_check_version (SHISHI_VERSION))
51 return GSASL_UNKNOWN_MECHANISM;
53 return GSASL_OK;
56 int
57 _gsasl_kerberos_v5_client_start (Gsasl_session * sctx, void **mech_data)
59 struct _Gsasl_kerberos_v5_client_state *state;
60 Gsasl_ctx *ctx;
61 int err;
63 state = malloc (sizeof (*state));
64 if (state == NULL)
65 return GSASL_MALLOC_ERROR;
67 memset (state, 0, sizeof (*state));
69 err = shishi_init (&state->sh);
70 if (err)
71 return GSASL_KERBEROS_V5_INIT_ERROR;
73 state->step = 0;
74 state->clientqop = GSASL_QOP_AUTH_INT;
76 *mech_data = state;
78 return GSASL_OK;
81 #define STEP_FIRST 0
82 #define STEP_NONINFRA_SEND_ASREQ 1
83 #define STEP_NONINFRA_WAIT_ASREP 2
84 #define STEP_NONINFRA_SEND_APREQ 3
85 #define STEP_NONINFRA_WAIT_APREP 4
86 #define STEP_SUCCESS 5
88 int
89 _gsasl_kerberos_v5_client_step (Gsasl_session * sctx,
90 void *mech_data,
91 const char *input,
92 size_t input_len,
93 char *output, size_t * output_len)
95 struct _Gsasl_kerberos_v5_client_state *state = mech_data;
96 Gsasl_client_callback_authentication_id cb_authentication_id;
97 Gsasl_client_callback_authorization_id cb_authorization_id;
98 Gsasl_client_callback_qop cb_qop;
99 Gsasl_client_callback_realm cb_realm;
100 Gsasl_client_callback_password cb_password;
101 Gsasl_client_callback_service cb_service;
102 Gsasl_client_callback_maxbuf cb_maxbuf;
103 Gsasl_ctx *ctx;
104 int res;
105 int len;
107 ctx = gsasl_client_ctx_get (sctx);
108 if (ctx == NULL)
109 return GSASL_CANNOT_GET_CTX;
111 /* These are optional */
112 cb_realm = gsasl_client_callback_realm_get (ctx);
113 cb_service = gsasl_client_callback_service_get (ctx);
114 cb_authentication_id = gsasl_client_callback_authentication_id_get (ctx);
115 cb_authorization_id = gsasl_client_callback_authorization_id_get (ctx);
116 cb_qop = gsasl_client_callback_qop_get (ctx);
117 cb_maxbuf = gsasl_client_callback_maxbuf_get (ctx);
119 /* Only optionally needed in infrastructure mode */
120 cb_password = gsasl_client_callback_password_get (ctx);
121 if (cb_password == NULL)
122 return GSASL_NEED_CLIENT_PASSWORD_CALLBACK;
124 /* I think we really need this one */
125 cb_service = gsasl_client_callback_service_get (ctx);
126 if (cb_service == NULL)
127 return GSASL_NEED_CLIENT_SERVICE_CALLBACK;
129 switch (state->step)
131 case STEP_FIRST:
132 if (input == NULL)
134 *output_len = 0;
135 return GSASL_NEEDS_MORE;
138 if (input_len != SERVER_HELLO_LEN)
139 return GSASL_MECHANISM_PARSE_ERROR;
141 memcpy (state->serverhello, input, input_len);
144 unsigned char serverbitmap;
146 memcpy (&serverbitmap, input, BITMAP_LEN);
147 state->serverqops = 0;
148 if (serverbitmap & GSASL_QOP_AUTH)
149 state->serverqops |= GSASL_QOP_AUTH;
150 if (serverbitmap & GSASL_QOP_AUTH_INT)
151 state->serverqops |= GSASL_QOP_AUTH_INT;
152 if (serverbitmap & GSASL_QOP_AUTH_CONF)
153 state->serverqops |= GSASL_QOP_AUTH_CONF;
154 if (serverbitmap & MUTUAL)
155 state->servermutual = 1;
157 memcpy (&state->servermaxbuf, &input[BITMAP_LEN], MAXBUF_LEN);
158 state->servermaxbuf = ntohl (state->servermaxbuf);
160 if (cb_qop)
161 state->clientqop = cb_qop (sctx, state->serverqops);
163 if (!(state->serverqops & state->clientqop &
164 (GSASL_QOP_AUTH | GSASL_QOP_AUTH_INT | GSASL_QOP_AUTH_CONF)))
165 return GSASL_AUTHENTICATION_ERROR;
167 /* XXX for now we require server authentication */
168 if (!state->servermutual)
169 return GSASL_AUTHENTICATION_ERROR;
171 /* Decide policy here: non-infrastructure, infrastructure or proxy.
173 * A callback to decide should be added, but without the default
174 * should be:
176 * IF shishi_tktset_get_for_server() THEN
177 * INFRASTRUCTURE MODE
178 * ELSE IF shishi_realm_for_server(server) THEN
179 * PROXY INFRASTRUCTURE (then fallback to NIM?)
180 * ELSE
181 * NON-INFRASTRUCTURE MODE
183 state->step = STEP_NONINFRA_SEND_APREQ; /* only NIM for now.. */
184 /* fall through */
186 case STEP_NONINFRA_SEND_ASREQ:
187 res = shishi_as (state->sh, &state->as);
188 if (res)
189 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
191 if (cb_authentication_id) /* Shishi defaults to one otherwise */
193 len = *output_len - 1;
194 res = cb_authentication_id (sctx, output, &len);
195 if (res != GSASL_OK)
196 return res;
197 output[len] = '\0';
199 res = shishi_kdcreq_set_cname (state->sh, shishi_as_req (state->as),
200 SHISHI_NT_UNKNOWN, output);
201 if (res != GSASL_OK)
202 return res;
205 if (cb_realm)
207 len = *output_len - 1;
208 res = cb_realm (sctx, output, &len);
209 if (res != GSASL_OK)
210 return res;
212 else
213 len = 0;
215 output[len] = '\0';
216 res = shishi_kdcreq_set_realm (state->sh, shishi_as_req (state->as),
217 output);
218 if (res != GSASL_OK)
219 return res;
221 if (cb_service)
223 char *sname[3];
224 size_t servicelen = 0;
225 size_t hostnamelen = 0;
227 res = cb_service (sctx, NULL, &servicelen, NULL, &hostnamelen,
228 /* XXX support servicename a'la DIGEST-MD5 too? */
229 NULL, NULL);
230 if (res != GSASL_OK)
231 return res;
233 if (*output_len < servicelen + 1 + hostnamelen + 1)
234 return GSASL_TOO_SMALL_BUFFER;
236 sname[0] = &output[0];
237 sname[1] = &output[servicelen + 2];
238 sname[2] = NULL;
240 res = cb_service (sctx, sname[0], &servicelen,
241 sname[1], &hostnamelen, NULL, NULL);
242 if (res != GSASL_OK)
243 return res;
245 sname[0][servicelen] = '\0';
246 sname[1][hostnamelen] = '\0';
248 res = shishi_kdcreq_set_sname (state->sh, shishi_as_req (state->as),
249 SHISHI_NT_UNKNOWN, sname);
250 if (res != GSASL_OK)
251 return res;
254 /* XXX query application for encryption types and set the etype
255 field? Already configured by shishi though... */
257 res = shishi_a2d (state->sh, shishi_as_req (state->as),
258 output, output_len);
259 if (res != SHISHI_OK)
260 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
262 state->step = STEP_NONINFRA_WAIT_ASREP;
264 res = GSASL_NEEDS_MORE;
265 break;
267 case STEP_NONINFRA_WAIT_ASREP:
268 if (shishi_as_rep_der_set (state->as, input, input_len) != SHISHI_OK)
269 return GSASL_MECHANISM_PARSE_ERROR;
271 /* XXX? password stored in callee's output buffer */
272 len = *output_len - 1;
273 res = cb_password (sctx, output, &len);
274 if (res != GSASL_OK && res != GSASL_NEEDS_MORE)
275 return res;
276 output[len] = '\0';
278 res = shishi_as_rep_process (state->as, NULL, output);
279 if (res != SHISHI_OK)
280 return GSASL_AUTHENTICATION_ERROR;
282 state->step = STEP_NONINFRA_SEND_APREQ;
283 /* fall through */
285 case STEP_NONINFRA_SEND_APREQ:
286 if (*output_len <= CLIENT_HELLO_LEN + SERVER_HELLO_LEN)
287 return GSASL_TOO_SMALL_BUFFER;
289 if (!(state->clientqop & ~GSASL_QOP_AUTH))
290 state->clientmaxbuf = 0;
291 else if (cb_maxbuf)
292 state->clientmaxbuf = cb_maxbuf (sctx, state->servermaxbuf);
293 else
294 state->clientmaxbuf = MAXBUF_DEFAULT;
296 /* XXX for now we require server authentication */
297 output[0] = state->clientqop | MUTUAL;
299 uint32_t tmp;
301 tmp = ntohl (state->clientmaxbuf);
302 memcpy (&output[BITMAP_LEN], &tmp, MAXBUF_LEN);
304 memcpy (&output[CLIENT_HELLO_LEN], state->serverhello,
305 SERVER_HELLO_LEN);
307 if (cb_authorization_id)
309 len = *output_len - CLIENT_HELLO_LEN + SERVER_HELLO_LEN;
310 res = cb_authorization_id (sctx, &output[CLIENT_HELLO_LEN +
311 SERVER_HELLO_LEN], &len);
313 else
314 len = 0;
316 len += CLIENT_HELLO_LEN + SERVER_HELLO_LEN;
317 res = shishi_ap_tktoptionsdata (state->sh,
318 &state->ap,
319 shishi_as_tkt (state->as),
320 SHISHI_APOPTIONS_MUTUAL_REQUIRED,
321 output, len);
322 if (res != SHISHI_OK)
323 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
325 res = shishi_authenticator_add_authorizationdata
326 (state->sh, shishi_ap_authenticator (state->ap), -1, output, len);
327 if (res != SHISHI_OK)
328 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
330 /* XXX set realm in AP-REQ and Authenticator */
332 res = shishi_ap_req_der (state->ap, output, output_len);
333 if (res != SHISHI_OK)
334 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
336 state->step = STEP_NONINFRA_WAIT_APREP;
338 res = GSASL_NEEDS_MORE;
339 break;
341 case STEP_NONINFRA_WAIT_APREP:
342 if (shishi_ap_rep_der_set (state->ap, input, input_len) != SHISHI_OK)
343 return GSASL_MECHANISM_PARSE_ERROR;
345 res = shishi_ap_rep_verify (state->ap);
346 if (res != SHISHI_OK)
347 return GSASL_AUTHENTICATION_ERROR;
349 state->step = STEP_SUCCESS;
351 /* XXX support AP session keys */
352 state->sessionkey = shishi_tkt_key (shishi_as_tkt (state->as));
354 *output_len = 0;
355 res = GSASL_OK;
356 break;
358 default:
359 res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES;
360 break;
363 return res;
367 _gsasl_kerberos_v5_client_encode (Gsasl_session * sctx,
368 void *mech_data,
369 const char *input,
370 size_t input_len,
371 char **output, size_t * output_len)
373 struct _Gsasl_kerberos_v5_client_state *state = mech_data;
374 int res;
376 if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
378 return GSASL_INTEGRITY_ERROR;
380 else if (state && state->sessionkey
381 && state->clientqop & GSASL_QOP_AUTH_INT)
383 res = shishi_safe (state->sh, &state->safe);
384 if (res != SHISHI_OK)
385 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
387 res = shishi_safe_set_user_data (state->sh,
388 shishi_safe_safe (state->safe),
389 input, input_len);
390 if (res != SHISHI_OK)
391 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
393 res = shishi_safe_build (state->safe, state->sessionkey);
394 if (res != SHISHI_OK)
395 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
397 res = shishi_safe_safe_der (state->safe, output, output_len);
398 if (res != SHISHI_OK)
399 return GSASL_KERBEROS_V5_INTERNAL_ERROR;
401 else
403 *output_len = input_len;
404 *output = malloc (input_len);
405 if (!*output)
406 return GSASL_MALLOC_ERROR;
407 memcpy (*output, input, input_len);
410 return GSASL_OK;
414 _gsasl_kerberos_v5_client_decode (Gsasl_session * sctx,
415 void *mech_data,
416 const char *input,
417 size_t input_len,
418 char *output, size_t * output_len)
420 struct _Gsasl_kerberos_v5_client_state *state = mech_data;
422 if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
424 return GSASL_INTEGRITY_ERROR;
426 else if (state && state->sessionkey
427 && state->clientqop & GSASL_QOP_AUTH_INT)
429 return GSASL_INTEGRITY_ERROR;
431 else
433 *output_len = input_len;
434 *output = malloc (input_len);
435 if (!*output)
436 return GSASL_MALLOC_ERROR;
437 memcpy (*output, input, input_len);
440 return GSASL_OK;
444 _gsasl_kerberos_v5_client_finish (Gsasl_session * sctx, void *mech_data)
446 struct _Gsasl_kerberos_v5_client_state *state = mech_data;
448 shishi_done (state->sh);
449 free (state);
451 return GSASL_OK;