Initial release, version 0.0.0.
[gsasl.git] / lib / gssapi.c
blob40ba63283cc90eb87f544023711c75d31f29f6d8
1 /* gssapi.c implementation of SASL mechanism GSSAPI from RFC 2222
2 * Copyright (C) 2002 Simon Josefsson
4 * This file is part of libgsasl.
6 * Libgsasl is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * Libgsasl 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 libgsasl; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "internal.h"
24 #ifdef USE_GSSAPI
26 #ifdef HAVE_GSSAPI_H
27 #include <gssapi.h>
28 #endif
29 #ifdef HAVE_GSSAPI_GSSAPI_H
30 #include <gssapi/gssapi.h>
31 #endif
32 #ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H
33 #include <gssapi/gssapi_generic.h>
34 #endif
35 #ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE
36 #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
37 #endif
39 #ifdef HAVE_NETINET_IN_H
40 #include <netinet/in.h>
41 #endif
43 #define GSSAPI_AUTH_NONE 1
44 #define GSSAPI_AUTH_INTEGRITY 2
45 #define GSSAPI_AUTH_PRIVACY 4
47 /* Client */
49 struct _Gsasl_gssapi_client_state {
50 int step;
51 gss_name_t service;
52 gss_ctx_id_t context;
54 typedef struct _Gsasl_gssapi_client_state _Gsasl_gssapi_client_state;
56 int
57 _gsasl_gssapi_client_init (Gsasl_ctx *ctx)
59 return GSASL_OK;
62 void
63 _gsasl_gssapi_client_done (Gsasl_ctx *ctx)
65 return;
68 int
69 _gsasl_gssapi_client_start (Gsasl_session_ctx *cctx,
70 void **mech_data)
72 _Gsasl_gssapi_client_state *state;
73 Gsasl_ctx *ctx;
75 ctx = gsasl_client_ctx_get (cctx);
76 if (ctx == NULL)
77 return GSASL_CANNOT_GET_CTX;
79 if (gsasl_client_callback_authentication_id_get (ctx) == NULL)
80 return GSASL_NEED_CLIENT_AUTHENTICATION_ID_CALLBACK;
82 if (gsasl_client_callback_service_get (ctx) == NULL)
83 return GSASL_NEED_CLIENT_SERVICE_CALLBACK;
85 state = (_Gsasl_gssapi_client_state*) malloc(sizeof(*state));
86 if (state == NULL)
87 return GSASL_MALLOC_ERROR;
89 state->context = GSS_C_NO_CONTEXT;
90 state->service = NULL;
91 state->step = 0;
93 *mech_data = state;
95 return GSASL_OK;
98 int
99 _gsasl_gssapi_client_step (Gsasl_session_ctx *cctx,
100 void *mech_data,
101 const char *input,
102 size_t input_len,
103 char *output,
104 size_t *output_len)
106 _Gsasl_gssapi_client_state *state = mech_data;
107 Gsasl_client_callback_authentication_id cb_authentication_id;
108 Gsasl_client_callback_service cb_service;
109 Gsasl_ctx *ctx;
110 gss_buffer_desc bufdesc, bufdesc2;
111 gss_buffer_t buf = GSS_C_NO_BUFFER;
112 OM_uint32 maj_stat, min_stat;
113 gss_qop_t qop_state;
114 int conf_state;
115 int res;
117 ctx = gsasl_client_ctx_get (cctx);
118 if (ctx == NULL)
119 return GSASL_CANNOT_GET_CTX;
121 cb_authentication_id = gsasl_client_callback_authentication_id_get (ctx);
122 if (cb_authentication_id == NULL)
123 return GSASL_NEED_CLIENT_AUTHENTICATION_ID_CALLBACK;
125 cb_service = gsasl_client_callback_service_get (ctx);
126 if (cb_service == NULL)
127 return GSASL_NEED_CLIENT_SERVICE_CALLBACK;
129 if (state->service == NULL)
131 size_t servicelen = 0;
132 size_t hostnamelen = 0;
134 res = cb_service (cctx, NULL, &servicelen,
135 NULL, &hostnamelen,
136 NULL, NULL);
137 if (res != GSASL_OK)
138 return res;
140 bufdesc.length = servicelen + strlen("@") + hostnamelen + 1;
141 bufdesc.value = malloc(bufdesc.length);
142 if (bufdesc.value == NULL)
143 return GSASL_MALLOC_ERROR;
145 res = cb_service (cctx, (char*)bufdesc.value, &servicelen,
146 (char*)bufdesc.value + 1 + servicelen, &hostnamelen,
147 NULL, NULL);
148 if (res != GSASL_OK)
150 free(bufdesc.value);
151 return res;
153 ((char*)bufdesc.value)[servicelen] = '@';
154 ((char*)bufdesc.value)[bufdesc.length-1] = '\0';
156 maj_stat = gss_import_name(&min_stat, &bufdesc,
157 GSS_C_NT_HOSTBASED_SERVICE, &state->service);
158 free(bufdesc.value);
159 if (maj_stat != GSS_S_COMPLETE)
161 free(state);
162 return GSASL_GSSAPI_IMPORT_NAME_ERROR;
166 switch (state->step)
168 case 1:
169 bufdesc.length = input_len;
170 bufdesc.value = input;
171 buf = &bufdesc;
172 /* fall through */
174 case 0:
175 bufdesc2.length = 0;
176 bufdesc2.value = NULL;
177 maj_stat = gss_init_sec_context(&min_stat,
178 GSS_C_NO_CREDENTIAL,
179 &state->context,
180 state->service,
181 GSS_C_NO_OID,
182 GSS_C_MUTUAL_FLAG |
183 GSS_C_REPLAY_FLAG |
184 GSS_C_SEQUENCE_FLAG,
186 GSS_C_NO_CHANNEL_BINDINGS,
187 buf,
188 NULL,
189 &bufdesc2,
190 NULL,
191 NULL);
192 if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
193 return GSASL_GSSAPI_INIT_SEC_CONTEXT_ERROR;
195 if (*output_len < bufdesc2.length)
197 maj_stat = gss_release_buffer(&min_stat, &bufdesc2);
198 return GSASL_TOO_SMALL_BUFFER;
201 *output_len = bufdesc2.length;
202 memcpy(output, bufdesc2.value, bufdesc2.length);
204 if (maj_stat == GSS_S_COMPLETE)
205 state->step = 2;
206 else
207 state->step = 1;
209 maj_stat = gss_release_buffer(&min_stat, &bufdesc2);
210 if (maj_stat != GSS_S_COMPLETE)
211 return GSASL_GSSAPI_RELEASE_BUFFER_ERROR;
213 res = GSASL_NEEDS_MORE;
214 break;
216 case 2:
217 if (*output_len <= 4)
218 return GSASL_TOO_SMALL_BUFFER;
220 /* The client passes this token to GSS_Unwrap and interprets the
221 first octet of resulting cleartext as a bit-mask specifying
222 the security layers supported by the server and the second
223 through fourth octets as the maximum size output_message to
224 send to the server. The client then constructs data, with
225 the first octet containing the bit-mask specifying the
226 selected security layer, the second through fourth octets
227 containing in network byte order the maximum size
228 output_message the client is able to receive, and the
229 remaining octets containing the authorization identity. The
230 client passes the data to GSS_Wrap with conf_flag set to
231 FALSE, and responds with the generated output_message. The
232 client can then consider the server authenticated. */
234 bufdesc.length = input_len;
235 bufdesc.value = input;
236 maj_stat = gss_unwrap(&min_stat, state->context, &bufdesc,
237 &bufdesc2, &conf_state, &qop_state);
238 if (maj_stat != GSS_S_COMPLETE)
239 return GSASL_GSSAPI_UNWRAP_ERROR;
241 memcpy(output, bufdesc2.value, 4);
242 maj_stat = gss_release_buffer(&min_stat, &bufdesc2);
243 if (maj_stat != GSS_S_COMPLETE)
244 return GSASL_GSSAPI_RELEASE_BUFFER_ERROR;
246 if ((output[0] & GSSAPI_AUTH_NONE) == 0)
247 /* Integrity or privacy unsupported. */
248 return GSASL_GSSAPI_UNSUPPORTED_PROTECTION_ERROR;
250 output[0] = GSSAPI_AUTH_NONE;
251 bufdesc.length = *output_len - 4;
252 cb_authentication_id (cctx, output+4, &bufdesc.length);
253 bufdesc.length += 4;
254 bufdesc.value = output;
255 maj_stat = gss_wrap(&min_stat, state->context, 0, GSS_C_QOP_DEFAULT,
256 &bufdesc, &conf_state, &bufdesc2);
257 if (maj_stat != GSS_S_COMPLETE)
258 return GSASL_GSSAPI_WRAP_ERROR;
259 memcpy(output, bufdesc2.value, bufdesc2.length);
260 *output_len = bufdesc2.length;
262 maj_stat = gss_release_buffer(&min_stat, &bufdesc2);
263 if (maj_stat != GSS_S_COMPLETE)
264 return GSASL_GSSAPI_RELEASE_BUFFER_ERROR;
266 state->step++;
267 res = GSASL_NEEDS_MORE;
268 break;
270 default:
271 res = GSASL_OK;
272 break;
275 return res;
279 _gsasl_gssapi_client_finish (Gsasl_session_ctx *cctx,
280 void *mech_data)
282 _Gsasl_gssapi_client_state *state = mech_data;
283 OM_uint32 maj_stat, min_stat;
285 maj_stat = gss_release_name(&min_stat, &state->service);
286 if (state->context != GSS_C_NO_CONTEXT)
287 maj_stat = gss_delete_sec_context(&min_stat, state->context,
288 GSS_C_NO_BUFFER);
290 free(state);
292 return GSASL_OK;
295 /* Server */
297 struct _Gsasl_gssapi_server_state {
298 int step;
299 gss_name_t client;
300 gss_cred_id_t cred;
301 gss_ctx_id_t context;
303 typedef struct _Gsasl_gssapi_server_state _Gsasl_gssapi_server_state;
306 _gsasl_gssapi_server_init (Gsasl_ctx *ctx)
308 return GSASL_OK;
311 void
312 _gsasl_gssapi_server_done (Gsasl_ctx *ctx)
314 return;
318 _gsasl_gssapi_server_start (Gsasl_session_ctx *sctx,
319 void **mech_data)
321 _Gsasl_gssapi_server_state *state;
322 Gsasl_server_callback_service cb_service;
323 Gsasl_ctx *ctx;
324 OM_uint32 maj_stat, min_stat;
325 gss_name_t server;
326 gss_buffer_desc bufdesc;
327 size_t servicelen = 0;
328 size_t hostnamelen = 0;
329 int res;
331 ctx = gsasl_server_ctx_get (sctx);
332 if (ctx == NULL)
333 return GSASL_CANNOT_GET_CTX;
335 cb_service = gsasl_server_callback_service_get (ctx);
336 if (cb_service == NULL)
337 return GSASL_NEED_SERVER_SERVICE_CALLBACK;
339 if (gsasl_server_callback_gssapi_get (ctx) == NULL)
340 return GSASL_NEED_SERVER_GSSAPI_CALLBACK;
342 res = cb_service (sctx, NULL, &servicelen,
343 NULL, &hostnamelen);
344 if (res != GSASL_OK)
345 return res;
347 bufdesc.length = servicelen + strlen("@") + hostnamelen + 1;
348 bufdesc.value = malloc(bufdesc.length);
349 if (bufdesc.value == NULL)
350 return GSASL_MALLOC_ERROR;
352 res = cb_service (sctx, bufdesc.value, &servicelen,
353 (char*)bufdesc.value + 1 + servicelen, &hostnamelen);
354 if (res != GSASL_OK)
356 free(bufdesc.value);
357 return res;
359 ((char*)bufdesc.value)[servicelen] = '@';
360 ((char*)bufdesc.value)[bufdesc.length-1] = '\0';
362 state = (_Gsasl_gssapi_server_state*) malloc(sizeof(*state));
363 if (state == NULL)
365 free(bufdesc.value);
366 return GSASL_MALLOC_ERROR;
369 maj_stat = gss_import_name(&min_stat, &bufdesc, GSS_C_NT_HOSTBASED_SERVICE,
370 &server);
371 free(bufdesc.value);
372 if (maj_stat != GSS_S_COMPLETE)
374 free(state);
375 return GSASL_GSSAPI_IMPORT_NAME_ERROR;
378 maj_stat = gss_acquire_cred (&min_stat, server, 0,
379 GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
380 &state->cred, NULL, NULL);
381 gss_release_name(&min_stat, &server);
383 if (maj_stat != GSS_S_COMPLETE)
385 free(state);
386 return GSASL_GSSAPI_ACQUIRE_CRED_ERROR;
389 state->step = 0;
390 state->context = GSS_C_NO_CONTEXT;
391 state->client = NULL;
392 *mech_data = state;
394 return GSASL_OK;
398 _gsasl_gssapi_server_step (Gsasl_session_ctx *sctx,
399 void *mech_data,
400 const char *input,
401 size_t input_len,
402 char *output,
403 size_t *output_len)
405 _Gsasl_gssapi_server_state *state = mech_data;
406 Gsasl_server_callback_gssapi cb_gssapi;
407 gss_buffer_desc bufdesc1, bufdesc2;
408 OM_uint32 maj_stat, min_stat;
409 gss_buffer_desc client_name;
410 gss_OID mech_type;
411 Gsasl_ctx *ctx;
412 char *username;
413 int res;
415 ctx = gsasl_server_ctx_get (sctx);
416 if (ctx == NULL)
417 return GSASL_CANNOT_GET_CTX;
419 cb_gssapi = gsasl_server_callback_gssapi_get (ctx);
420 if (cb_gssapi == NULL)
421 return GSASL_NEED_SERVER_GSSAPI_CALLBACK;
423 switch (state->step)
425 case 0:
426 if (input_len == 0)
428 *output_len = 0;
429 return GSASL_NEEDS_MORE;
431 state->step++;
432 /* fall through */
434 case 1:
435 bufdesc1.value = input;
436 bufdesc1.length = input_len;
437 maj_stat = gss_accept_sec_context (&min_stat,
438 &state->context,
439 state->cred,
440 &bufdesc1,
441 GSS_C_NO_CHANNEL_BINDINGS,
442 &state->client,
443 &mech_type,
444 &bufdesc2,
445 NULL,
446 NULL,
447 NULL);
448 if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
450 return GSASL_GSSAPI_ACCEPT_SEC_CONTEXT_ERROR;
453 if (*output_len < bufdesc2.length)
455 maj_stat = gss_release_buffer (&min_stat, &bufdesc2);
456 return GSASL_TOO_SMALL_BUFFER;
459 if (maj_stat == GSS_S_COMPLETE)
460 state->step++;
462 memcpy(output, bufdesc2.value, bufdesc2.length);
463 *output_len = bufdesc2.length;
465 maj_stat = gss_release_buffer (&min_stat, &bufdesc2);
466 if (maj_stat != GSS_S_COMPLETE)
467 return GSASL_GSSAPI_RELEASE_BUFFER_ERROR;
469 res = GSASL_NEEDS_MORE;
470 break;
472 case 2:
473 if (*output_len < 4)
474 return GSASL_TOO_SMALL_BUFFER;
476 memset(output, 0xFF, 4);
477 output[0] = GSSAPI_AUTH_NONE;
478 bufdesc1.length = 4;
479 bufdesc1.value = output;
480 maj_stat = gss_wrap (&min_stat, state->context, 0, GSS_C_QOP_DEFAULT,
481 &bufdesc1, NULL, &bufdesc2);
482 if (maj_stat != GSS_S_COMPLETE)
483 return GSASL_GSSAPI_WRAP_ERROR;
485 if (*output_len < bufdesc2.length)
487 maj_stat = gss_release_buffer (&min_stat, &bufdesc2);
488 return GSASL_TOO_SMALL_BUFFER;
490 memcpy(output, bufdesc2.value, bufdesc2.length);
491 *output_len = bufdesc2.length;
493 maj_stat = gss_release_buffer (&min_stat, &bufdesc2);
494 if (maj_stat != GSS_S_COMPLETE)
495 return GSASL_GSSAPI_RELEASE_BUFFER_ERROR;
497 state->step++;
498 res = GSASL_NEEDS_MORE;
499 break;
501 case 3:
502 bufdesc1.value = input;
503 bufdesc1.length = input_len;
504 maj_stat = gss_unwrap (&min_stat, state->context, &bufdesc1,
505 &bufdesc2, NULL, NULL);
506 if (maj_stat != GSS_S_COMPLETE)
507 return GSASL_GSSAPI_UNWRAP_ERROR;
509 /* The client passes this token to GSS_Unwrap and interprets the
510 first octet of resulting cleartext as a bit-mask specifying
511 the security layers supported by the server and the second
512 through fourth octets as the maximum size output_message to
513 send to the server. The client then constructs data, with
514 the first octet containing the bit-mask specifying the
515 selected security layer, the second through fourth octets
516 containing in network byte order the maximum size
517 output_message the client is able to receive, and the
518 remaining octets containing the authorization identity. The
519 client passes the data to GSS_Wrap with conf_flag set to
520 FALSE, and responds with the generated output_message. The
521 client can then consider the server authenticated. */
523 if ((((char*)bufdesc2.value)[0] & GSSAPI_AUTH_NONE) == 0)
525 /* Integrity or privacy unsupported */
526 maj_stat = gss_release_buffer (&min_stat, &bufdesc2);
527 return GSASL_GSSAPI_UNSUPPORTED_PROTECTION_ERROR;
530 username = malloc(bufdesc2.length - 4 + 1);
531 if (username == NULL)
533 gss_release_buffer (&min_stat, &bufdesc2);
534 return GSASL_MALLOC_ERROR;
537 memcpy(username, (char*)bufdesc2.value + 4, bufdesc2.length - 4);
538 username[bufdesc2.length - 4] = '\0';
539 maj_stat = gss_release_buffer (&min_stat, &bufdesc2);
540 if (maj_stat != GSS_S_COMPLETE)
541 return GSASL_GSSAPI_RELEASE_BUFFER_ERROR;
543 maj_stat = gss_display_name(&min_stat, state->client,
544 &client_name, &mech_type);
545 if (maj_stat != GSS_S_COMPLETE)
547 free (username);
548 return GSASL_GSSAPI_DISPLAY_NAME_ERROR;
551 res = cb_gssapi(sctx, client_name.value, username);
552 free(username);
554 state->step++;
555 break;
557 default:
558 res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES;
559 break;
562 return res;
566 _gsasl_gssapi_server_finish (Gsasl_session_ctx *sctx,
567 void *mech_data)
569 _Gsasl_gssapi_server_state *state = mech_data;
570 OM_uint32 min_stat;
572 if (state->context != GSS_C_NO_CONTEXT)
573 gss_delete_sec_context (&min_stat, &state->context, GSS_C_NO_BUFFER);
575 if (state->cred != GSS_C_NO_CREDENTIAL)
576 gss_release_cred(&min_stat, state->cred);
578 free(state);
580 return GSASL_OK;
582 #endif /* USE_GSSAPI */