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
29 #ifdef HAVE_GSSAPI_GSSAPI_H
30 #include <gssapi/gssapi.h>
32 #ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H
33 #include <gssapi/gssapi_generic.h>
35 #ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE
36 #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
39 #ifdef HAVE_NETINET_IN_H
40 #include <netinet/in.h>
43 #define GSSAPI_AUTH_NONE 1
44 #define GSSAPI_AUTH_INTEGRITY 2
45 #define GSSAPI_AUTH_PRIVACY 4
49 struct _Gsasl_gssapi_client_state
{
54 typedef struct _Gsasl_gssapi_client_state _Gsasl_gssapi_client_state
;
57 _gsasl_gssapi_client_init (Gsasl_ctx
*ctx
)
63 _gsasl_gssapi_client_done (Gsasl_ctx
*ctx
)
69 _gsasl_gssapi_client_start (Gsasl_session_ctx
*cctx
,
72 _Gsasl_gssapi_client_state
*state
;
75 ctx
= gsasl_client_ctx_get (cctx
);
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
));
87 return GSASL_MALLOC_ERROR
;
89 state
->context
= GSS_C_NO_CONTEXT
;
90 state
->service
= NULL
;
99 _gsasl_gssapi_client_step (Gsasl_session_ctx
*cctx
,
106 _Gsasl_gssapi_client_state
*state
= mech_data
;
107 Gsasl_client_callback_authentication_id cb_authentication_id
;
108 Gsasl_client_callback_service cb_service
;
110 gss_buffer_desc bufdesc
, bufdesc2
;
111 gss_buffer_t buf
= GSS_C_NO_BUFFER
;
112 OM_uint32 maj_stat
, min_stat
;
117 ctx
= gsasl_client_ctx_get (cctx
);
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
,
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
,
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
);
159 if (maj_stat
!= GSS_S_COMPLETE
)
162 return GSASL_GSSAPI_IMPORT_NAME_ERROR
;
169 bufdesc
.length
= input_len
;
170 bufdesc
.value
= input
;
176 bufdesc2
.value
= NULL
;
177 maj_stat
= gss_init_sec_context(&min_stat
,
186 GSS_C_NO_CHANNEL_BINDINGS
,
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
)
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
;
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
);
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
;
267 res
= GSASL_NEEDS_MORE
;
279 _gsasl_gssapi_client_finish (Gsasl_session_ctx
*cctx
,
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
,
297 struct _Gsasl_gssapi_server_state
{
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
)
312 _gsasl_gssapi_server_done (Gsasl_ctx
*ctx
)
318 _gsasl_gssapi_server_start (Gsasl_session_ctx
*sctx
,
321 _Gsasl_gssapi_server_state
*state
;
322 Gsasl_server_callback_service cb_service
;
324 OM_uint32 maj_stat
, min_stat
;
326 gss_buffer_desc bufdesc
;
327 size_t servicelen
= 0;
328 size_t hostnamelen
= 0;
331 ctx
= gsasl_server_ctx_get (sctx
);
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
,
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
);
359 ((char*)bufdesc
.value
)[servicelen
] = '@';
360 ((char*)bufdesc
.value
)[bufdesc
.length
-1] = '\0';
362 state
= (_Gsasl_gssapi_server_state
*) malloc(sizeof(*state
));
366 return GSASL_MALLOC_ERROR
;
369 maj_stat
= gss_import_name(&min_stat
, &bufdesc
, GSS_C_NT_HOSTBASED_SERVICE
,
372 if (maj_stat
!= GSS_S_COMPLETE
)
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
)
386 return GSASL_GSSAPI_ACQUIRE_CRED_ERROR
;
390 state
->context
= GSS_C_NO_CONTEXT
;
391 state
->client
= NULL
;
398 _gsasl_gssapi_server_step (Gsasl_session_ctx
*sctx
,
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
;
415 ctx
= gsasl_server_ctx_get (sctx
);
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
;
429 return GSASL_NEEDS_MORE
;
435 bufdesc1
.value
= input
;
436 bufdesc1
.length
= input_len
;
437 maj_stat
= gss_accept_sec_context (&min_stat
,
441 GSS_C_NO_CHANNEL_BINDINGS
,
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
)
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
;
474 return GSASL_TOO_SMALL_BUFFER
;
476 memset(output
, 0xFF, 4);
477 output
[0] = GSSAPI_AUTH_NONE
;
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
;
498 res
= GSASL_NEEDS_MORE
;
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
)
548 return GSASL_GSSAPI_DISPLAY_NAME_ERROR
;
551 res
= cb_gssapi(sctx
, client_name
.value
, username
);
558 res
= GSASL_MECHANISM_CALLED_TOO_MANY_TIMES
;
566 _gsasl_gssapi_server_finish (Gsasl_session_ctx
*sctx
,
569 _Gsasl_gssapi_server_state
*state
= mech_data
;
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
);
582 #endif /* USE_GSSAPI */