3 * Rob Siemborski (SASL v2 Conversion)
4 * $Id: gssapi.c,v 1.92 2004/07/21 14:39:06 rjs3 Exp $
7 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
21 * 3. The name "Carnegie Mellon University" must not be used to
22 * endorse or promote products derived from this software without
23 * prior written permission. For permission or any other legal
24 * details, please contact
25 * Office of Technology Transfer
26 * Carnegie Mellon University
28 * Pittsburgh, PA 15213-3890
29 * (412) 268-4387, fax: (412) 268-7395
30 * tech-transfer@andrew.cmu.edu
32 * 4. Redistributions of any form whatsoever must retain the following
34 * "This product includes software developed by Computing Services
35 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
37 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
38 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
39 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
40 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
41 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
42 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
43 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
51 #include <gssapi/gssapi.h>
55 # include <winsock2.h>
60 /* we also need io.h for access() prototype */
63 # include <sys/param.h>
64 # include <sys/socket.h>
65 # include <netinet/in.h>
66 # include <arpa/inet.h>
75 #include "plugin_common.h"
83 /***************************** Common Section *****************************/
85 static const char plugin_id[] = "$Id: gssapi.c,v 1.92 2004/07/21 14:39:06 rjs3 Exp $";
87 static const char * GSSAPI_BLANK_STRING = "";
89 #ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE
90 extern gss_OID gss_nt_service_name;
91 #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
94 #ifdef WANT_KERBEROS5_3DES
95 /* Check if CyberSafe flag is defined */
96 #ifdef CSF_GSS_C_DES3_FLAG
97 #define K5_MAX_SSF 112
100 /* Heimdal and MIT use the following */
101 #ifdef GSS_KRB5_CONF_C_QOP_DES3_KD
102 #define K5_MAX_SSF 112
108 /* All Kerberos implementations support DES */
109 #define K5_MAX_SSF 56
112 /* GSSAPI SASL Mechanism by Leif Johansson <leifj@matematik.su.se>
113 * inspired by the kerberos mechanism and the gssapi_server and
114 * gssapi_client from the heimdal distribution by Assar Westerlund
115 * <assar@sics.se> and Johan Danielsson <joda@pdc.kth.se>.
116 * See the configure.in file for details on dependencies.
118 * Important contributions from Sam Hartman <hartmans@fundsxpress.com>.
120 * This code was tested with the following distributions of Kerberos:
121 * Heimdal (http://www.pdc.kth.se/heimdal), MIT (http://web.mit.edu/kerberos/www/)
122 * CyberSafe (http://www.cybersafe.com/) and SEAM.
125 #ifdef GSS_USE_MUTEXES
126 #define GSS_LOCK_MUTEX(utils) \
127 if(((sasl_utils_t *)(utils))->mutex_lock(gss_mutex) != 0) { \
131 #define GSS_UNLOCK_MUTEX(utils) \
132 if(((sasl_utils_t *)(utils))->mutex_unlock(gss_mutex) != 0) { \
136 static void *gss_mutex = NULL;
138 #define GSS_LOCK_MUTEX(utils)
139 #define GSS_UNLOCK_MUTEX(utils)
142 static gss_OID_desc gss_spnego_mechanism_oid_desc =
143 {6, (void *)"\x2b\x06\x01\x05\x05\x02"};
144 static gss_OID_desc gss_krb5_mechanism_oid_desc =
145 {9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
147 typedef struct context {
150 gss_ctx_id_t gss_ctx;
152 gss_name_t client_name;
153 gss_name_t server_name;
154 gss_cred_id_t server_creds;
155 gss_cred_id_t client_creds;
157 sasl_ssf_t limitssf, requiressf; /* application defined bounds, for the
159 const sasl_utils_t *utils;
161 /* layers buffering */
162 decode_context_t decode_context;
164 char *encode_buf; /* For encoding/decoding mem management */
166 char *decode_once_buf;
167 unsigned encode_buf_len;
168 unsigned decode_buf_len;
169 unsigned decode_once_buf_len;
170 buffer_info_t *enc_in_buf;
172 char *out_buf; /* per-step mem management */
173 unsigned out_buf_len;
175 char *authid; /* hold the authid between steps - server */
176 const char *user; /* hold the userid between steps - client */
183 SASL_GSSAPI_STATE_AUTHNEG = 1,
184 SASL_GSSAPI_STATE_SSFCAP = 2,
185 SASL_GSSAPI_STATE_SSFREQ = 3,
186 SASL_GSSAPI_STATE_AUTHENTICATED = 4
189 /* sasl_gss_log: only logs status string returned from gss_display_status() */
190 #define sasl_gss_log(x,y,z) sasl_gss_seterror_(x,y,z,1)
191 #define sasl_gss_seterror(x,y,z) sasl_gss_seterror_(x,y,z,0)
194 sasl_gss_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min,
197 OM_uint32 maj_stat, min_stat;
202 unsigned int len, curlen = 0;
203 const char prefix[] = "GSSAPI Error: ";
205 len = sizeof(prefix);
206 ret = _plug_buf_alloc(utils, &out, &curlen, 256);
207 if(ret != SASL_OK) return SASL_OK;
213 GSS_LOCK_MUTEX(utils);
214 maj_stat = gss_display_status(&min_stat, maj,
215 GSS_C_GSS_CODE, GSS_C_NULL_OID,
217 GSS_UNLOCK_MUTEX(utils);
219 if(GSS_ERROR(maj_stat)) {
221 utils->log(utils->conn, SASL_LOG_FAIL,
222 "GSSAPI Failure: (could not get major error message)");
224 utils->seterror(utils->conn, 0,
226 "(could not get major error message)");
232 len += len + msg.length;
233 ret = _plug_buf_alloc(utils, &out, &curlen, len);
240 strcat(out, msg.value);
242 GSS_LOCK_MUTEX(utils);
243 gss_release_buffer(&min_stat, &msg);
244 GSS_UNLOCK_MUTEX(utils);
250 /* Now get the minor status */
253 ret = _plug_buf_alloc(utils, &out, &curlen, len);
263 GSS_LOCK_MUTEX(utils);
264 maj_stat = gss_display_status(&min_stat, min,
265 GSS_C_MECH_CODE, GSS_C_NULL_OID,
267 GSS_UNLOCK_MUTEX(utils);
269 if(GSS_ERROR(maj_stat)) {
271 utils->log(utils->conn, SASL_LOG_FAIL,
272 "GSSAPI Failure: (could not get minor error message)");
274 utils->seterror(utils->conn, 0,
276 "(could not get minor error message)");
282 len += len + msg.length;
284 ret = _plug_buf_alloc(utils, &out, &curlen, len);
290 strcat(out, msg.value);
292 GSS_LOCK_MUTEX(utils);
293 gss_release_buffer(&min_stat, &msg);
294 GSS_UNLOCK_MUTEX(utils);
301 ret = _plug_buf_alloc(utils, &out, &curlen, len);
310 utils->log(utils->conn, SASL_LOG_FAIL, out);
312 utils->seterror(utils->conn, 0, out);
320 sasl_gss_encode(void *context, const struct iovec *invec, unsigned numiov,
321 const char **output, unsigned *outputlen, int privacy)
323 context_t *text = (context_t *)context;
324 OM_uint32 maj_stat, min_stat;
325 gss_buffer_t input_token, output_token;
326 gss_buffer_desc real_input_token, real_output_token;
328 struct buffer_info *inblob, bufinfo;
330 if(!output) return SASL_BADPARAM;
333 ret = _plug_iovec_to_buf(text->utils, invec, numiov, &text->enc_in_buf);
334 if(ret != SASL_OK) return ret;
335 inblob = text->enc_in_buf;
337 bufinfo.data = invec[0].iov_base;
338 bufinfo.curlen = invec[0].iov_len;
342 if (text->state != SASL_GSSAPI_STATE_AUTHENTICATED) return SASL_NOTDONE;
344 input_token = &real_input_token;
346 real_input_token.value = inblob->data;
347 real_input_token.length = inblob->curlen;
349 output_token = &real_output_token;
350 output_token->value = NULL;
351 output_token->length = 0;
353 GSS_LOCK_MUTEX(text->utils);
354 maj_stat = gss_wrap (&min_stat,
361 GSS_UNLOCK_MUTEX(text->utils);
363 if (GSS_ERROR(maj_stat))
365 sasl_gss_seterror(text->utils, maj_stat, min_stat);
366 if (output_token->value) {
367 GSS_LOCK_MUTEX(text->utils);
368 gss_release_buffer(&min_stat, output_token);
369 GSS_UNLOCK_MUTEX(text->utils);
374 if (output_token->value && output) {
377 ret = _plug_buf_alloc(text->utils, &(text->encode_buf),
378 &(text->encode_buf_len), output_token->length + 4);
380 if (ret != SASL_OK) {
381 GSS_LOCK_MUTEX(text->utils);
382 gss_release_buffer(&min_stat, output_token);
383 GSS_UNLOCK_MUTEX(text->utils);
387 len = htonl(output_token->length);
388 memcpy(text->encode_buf, &len, 4);
389 memcpy(text->encode_buf + 4, output_token->value, output_token->length);
393 *outputlen = output_token->length + 4;
396 *output = text->encode_buf;
398 if (output_token->value) {
399 GSS_LOCK_MUTEX(text->utils);
400 gss_release_buffer(&min_stat, output_token);
401 GSS_UNLOCK_MUTEX(text->utils);
406 static int gssapi_privacy_encode(void *context, const struct iovec *invec,
407 unsigned numiov, const char **output,
410 return sasl_gss_encode(context,invec,numiov,output,outputlen,1);
413 static int gssapi_integrity_encode(void *context, const struct iovec *invec,
414 unsigned numiov, const char **output,
417 return sasl_gss_encode(context,invec,numiov,output,outputlen,0);
420 static int gssapi_decode_packet(void *context,
421 const char *input, unsigned inputlen,
422 char **output, unsigned *outputlen)
424 context_t *text = (context_t *) context;
425 OM_uint32 maj_stat, min_stat;
426 gss_buffer_t input_token, output_token;
427 gss_buffer_desc real_input_token, real_output_token;
430 if (text->state != SASL_GSSAPI_STATE_AUTHENTICATED) {
431 SETERROR(text->utils, "GSSAPI Failure");
435 input_token = &real_input_token;
436 real_input_token.value = (char *) input;
437 real_input_token.length = inputlen;
439 output_token = &real_output_token;
440 output_token->value = NULL;
441 output_token->length = 0;
443 GSS_LOCK_MUTEX(text->utils);
444 maj_stat = gss_unwrap (&min_stat,
450 GSS_UNLOCK_MUTEX(text->utils);
452 if (GSS_ERROR(maj_stat))
454 sasl_gss_seterror(text->utils,maj_stat,min_stat);
455 if (output_token->value) {
456 GSS_LOCK_MUTEX(text->utils);
457 gss_release_buffer(&min_stat, output_token);
458 GSS_UNLOCK_MUTEX(text->utils);
464 *outputlen = output_token->length;
466 if (output_token->value) {
468 result = _plug_buf_alloc(text->utils, &text->decode_once_buf,
469 &text->decode_once_buf_len,
471 if(result != SASL_OK) {
472 GSS_LOCK_MUTEX(text->utils);
473 gss_release_buffer(&min_stat, output_token);
474 GSS_UNLOCK_MUTEX(text->utils);
477 *output = text->decode_once_buf;
478 memcpy(*output, output_token->value, *outputlen);
480 GSS_LOCK_MUTEX(text->utils);
481 gss_release_buffer(&min_stat, output_token);
482 GSS_UNLOCK_MUTEX(text->utils);
488 static int gssapi_decode(void *context,
489 const char *input, unsigned inputlen,
490 const char **output, unsigned *outputlen)
492 context_t *text = (context_t *) context;
495 ret = _plug_decode(&text->decode_context, input, inputlen,
496 &text->decode_buf, &text->decode_buf_len, outputlen,
497 gssapi_decode_packet, text);
499 *output = text->decode_buf;
504 static context_t *sasl_gss_new_context(const sasl_utils_t *utils)
508 ret = utils->malloc(sizeof(context_t));
509 if(!ret) return NULL;
511 memset(ret,0,sizeof(context_t));
517 static int sasl_gss_free_context_contents(context_t *text)
519 OM_uint32 maj_stat, min_stat;
521 if (!text) return SASL_OK;
523 GSS_LOCK_MUTEX(text->utils);
525 if (text->gss_ctx != GSS_C_NO_CONTEXT) {
526 maj_stat = gss_delete_sec_context(&min_stat,&text->gss_ctx,
528 text->gss_ctx = GSS_C_NO_CONTEXT;
531 if (text->client_name != GSS_C_NO_NAME) {
532 maj_stat = gss_release_name(&min_stat,&text->client_name);
533 text->client_name = GSS_C_NO_NAME;
536 if (text->server_name != GSS_C_NO_NAME) {
537 maj_stat = gss_release_name(&min_stat,&text->server_name);
538 text->server_name = GSS_C_NO_NAME;
541 if ( text->server_creds != GSS_C_NO_CREDENTIAL) {
542 maj_stat = gss_release_cred(&min_stat, &text->server_creds);
543 text->server_creds = GSS_C_NO_CREDENTIAL;
546 if ( text->client_creds != GSS_C_NO_CREDENTIAL) {
547 maj_stat = gss_release_cred(&min_stat, &text->client_creds);
548 text->client_creds = GSS_C_NO_CREDENTIAL;
551 GSS_UNLOCK_MUTEX(text->utils);
554 text->utils->free(text->out_buf);
555 text->out_buf = NULL;
558 if (text->encode_buf) {
559 text->utils->free(text->encode_buf);
560 text->encode_buf = NULL;
563 if (text->decode_buf) {
564 text->utils->free(text->decode_buf);
565 text->decode_buf = NULL;
568 if (text->decode_once_buf) {
569 text->utils->free(text->decode_once_buf);
570 text->decode_once_buf = NULL;
573 if (text->enc_in_buf) {
574 if(text->enc_in_buf->data) text->utils->free(text->enc_in_buf->data);
575 text->utils->free(text->enc_in_buf);
576 text->enc_in_buf = NULL;
579 _plug_decode_free(&text->decode_context);
581 if (text->authid) { /* works for both client and server */
582 text->utils->free(text->authid);
590 static void gssapi_common_mech_dispose(void *conn_context,
591 const sasl_utils_t *utils)
593 sasl_gss_free_context_contents((context_t *)(conn_context));
594 utils->free(conn_context);
597 static void gssapi_common_mech_free(void *global_context __attribute__((unused)),
598 const sasl_utils_t *utils)
600 #ifdef GSS_USE_MUTEXES
602 utils->mutex_free(gss_mutex);
608 /***************************** Server Section *****************************/
611 _gssapi_server_mech_new(void *glob_context __attribute__((unused)),
612 sasl_server_params_t *params,
613 const char *challenge __attribute__((unused)),
614 unsigned challen __attribute__((unused)),
620 text = sasl_gss_new_context(params->utils);
622 MEMERROR(params->utils);
626 text->gss_ctx = GSS_C_NO_CONTEXT;
627 text->client_name = GSS_C_NO_NAME;
628 text->server_name = GSS_C_NO_NAME;
629 text->server_creds = GSS_C_NO_CREDENTIAL;
630 text->client_creds = GSS_C_NO_CREDENTIAL;
631 text->state = SASL_GSSAPI_STATE_AUTHNEG;
632 text->rfc2222_gss = rfc2222_gss;
634 *conn_context = text;
640 gssapi_server_mech_new(void *glob_context,
641 sasl_server_params_t *params,
642 const char *challenge,
646 return _gssapi_server_mech_new(glob_context, params, challenge,
647 challen, 1, conn_context);
651 gss_spnego_server_mech_new(void *glob_context,
652 sasl_server_params_t *params,
653 const char *challenge,
657 return _gssapi_server_mech_new(glob_context, params, challenge,
658 challen, 0, conn_context);
662 gssapi_server_mech_step(void *conn_context,
663 sasl_server_params_t *params,
664 const char *clientin,
665 unsigned clientinlen,
666 const char **serverout,
667 unsigned *serveroutlen,
668 sasl_out_params_t *oparams)
670 context_t *text = (context_t *)conn_context;
671 gss_buffer_t input_token, output_token;
672 gss_buffer_desc real_input_token, real_output_token;
673 OM_uint32 maj_stat = 0, min_stat = 0;
675 gss_buffer_desc name_token;
677 OM_uint32 out_flags = 0 ;
680 input_token = &real_input_token;
681 output_token = &real_output_token;
682 output_token->value = NULL; output_token->length = 0;
683 input_token->value = NULL; input_token->length = 0;
686 PARAMERROR(text->utils);
687 return SASL_BADPARAM;
693 switch (text->state) {
695 case SASL_GSSAPI_STATE_AUTHNEG:
696 if (text->server_name == GSS_C_NO_NAME) { /* only once */
697 name_token.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
698 name_token.value = (char *)params->utils->malloc((name_token.length + 1) * sizeof(char));
699 if (name_token.value == NULL) {
700 MEMERROR(text->utils);
701 sasl_gss_free_context_contents(text);
704 sprintf(name_token.value,"%s@%s", params->service, params->serverFQDN);
706 GSS_LOCK_MUTEX(params->utils);
707 maj_stat = gss_import_name (&min_stat,
709 GSS_C_NT_HOSTBASED_SERVICE,
711 GSS_UNLOCK_MUTEX(params->utils);
713 params->utils->free(name_token.value);
714 name_token.value = NULL;
716 if (GSS_ERROR(maj_stat)) {
717 sasl_gss_seterror(text->utils, maj_stat, min_stat);
718 sasl_gss_free_context_contents(text);
722 if ( text->server_creds != GSS_C_NO_CREDENTIAL) {
723 GSS_LOCK_MUTEX(params->utils);
724 maj_stat = gss_release_cred(&min_stat, &text->server_creds);
725 GSS_UNLOCK_MUTEX(params->utils);
726 text->server_creds = GSS_C_NO_CREDENTIAL;
729 GSS_LOCK_MUTEX(params->utils);
730 maj_stat = gss_acquire_cred(&min_stat,
738 GSS_UNLOCK_MUTEX(params->utils);
740 if (GSS_ERROR(maj_stat)) {
741 sasl_gss_seterror(text->utils, maj_stat, min_stat);
742 sasl_gss_free_context_contents(text);
748 real_input_token.value = (void *)clientin;
749 real_input_token.length = clientinlen;
753 GSS_LOCK_MUTEX(params->utils);
755 gss_accept_sec_context(&min_stat,
757 params->gss_creds ? params->gss_creds : text->server_creds,
759 GSS_C_NO_CHANNEL_BINDINGS,
765 &(text->client_creds));
766 GSS_UNLOCK_MUTEX(params->utils);
768 if (GSS_ERROR(maj_stat)) {
769 sasl_gss_log(text->utils, maj_stat, min_stat);
770 text->utils->seterror(text->utils->conn, SASL_NOLOG, "GSSAPI Failure: gss_accept_sec_context");
771 if (output_token->value) {
772 GSS_LOCK_MUTEX(params->utils);
773 gss_release_buffer(&min_stat, output_token);
774 GSS_UNLOCK_MUTEX(params->utils);
776 sasl_gss_free_context_contents(text);
781 if ((params->props.security_flags & SASL_SEC_PASS_CREDENTIALS) &&
782 (!(out_flags & GSS_C_DELEG_FLAG) ||
783 text->client_creds == GSS_C_NO_CREDENTIAL) )
785 text->utils->seterror(text->utils->conn, SASL_LOG_WARN,
786 "GSSAPI warning: no credentials were passed");
787 /* continue with authentication */
791 *serveroutlen = output_token->length;
792 if (output_token->value) {
794 ret = _plug_buf_alloc(text->utils, &(text->out_buf),
795 &(text->out_buf_len), *serveroutlen);
797 GSS_LOCK_MUTEX(params->utils);
798 gss_release_buffer(&min_stat, output_token);
799 GSS_UNLOCK_MUTEX(params->utils);
802 memcpy(text->out_buf, output_token->value, *serveroutlen);
803 *serverout = text->out_buf;
806 GSS_LOCK_MUTEX(params->utils);
807 gss_release_buffer(&min_stat, output_token);
808 GSS_UNLOCK_MUTEX(params->utils);
810 /* No output token, send an empty string */
811 *serverout = GSSAPI_BLANK_STRING;
815 if (maj_stat == GSS_S_COMPLETE) {
816 if (text->rfc2222_gss) {
817 /* Switch to ssf negotiation */
818 text->state = SASL_GSSAPI_STATE_SSFCAP;
819 return SASL_CONTINUE;
822 return SASL_CONTINUE;
824 /* Fall-through for non-RFC 2222 mechanisms such as GSS-SPNEGO */
826 case SASL_GSSAPI_STATE_SSFCAP: {
827 unsigned char sasldata[4];
828 gss_buffer_desc name_token;
829 gss_buffer_desc name_without_realm;
830 gss_name_t without = NULL;
833 name_token.value = NULL;
834 name_without_realm.value = NULL;
836 /* We ignore whatever the client sent us at this stage */
838 GSS_LOCK_MUTEX(params->utils);
839 maj_stat = gss_display_name (&min_stat,
843 GSS_UNLOCK_MUTEX(params->utils);
845 if (GSS_ERROR(maj_stat)) {
847 GSS_LOCK_MUTEX(params->utils);
848 gss_release_name(&min_stat, &without);
849 GSS_UNLOCK_MUTEX(params->utils);
851 SETERROR(text->utils, "GSSAPI Failure");
852 sasl_gss_free_context_contents(text);
856 /* If the id contains a realm get the identifier for the user
857 without the realm and see if it's the same id (i.e.
858 tmartin == tmartin@ANDREW.CMU.EDU. If this is the case we just want
859 to return the id (i.e. just "tmartin" */
860 if (strchr((char *) name_token.value, (int) '@') != NULL) {
861 /* NOTE: libc malloc, as it is freed below by a gssapi internal
863 name_without_realm.value = params->utils->malloc(strlen(name_token.value)+1);
864 if (name_without_realm.value == NULL) {
865 if (name_token.value) {
866 GSS_LOCK_MUTEX(params->utils);
867 gss_release_buffer(&min_stat, &name_token);
868 GSS_UNLOCK_MUTEX(params->utils);
870 MEMERROR(text->utils);
874 strcpy(name_without_realm.value, name_token.value);
876 /* cut off string at '@' */
877 (strchr(name_without_realm.value,'@'))[0] = '\0';
879 name_without_realm.length = strlen( (char *) name_without_realm.value );
881 GSS_LOCK_MUTEX(params->utils);
882 maj_stat = gss_import_name (&min_stat,
884 /* Solaris 8/9 gss_import_name doesn't accept GSS_C_NULL_OID here,
885 so use GSS_C_NT_USER_NAME instead if available. */
886 #ifdef HAVE_GSS_C_NT_USER_NAME
892 GSS_UNLOCK_MUTEX(params->utils);
894 if (GSS_ERROR(maj_stat)) {
895 params->utils->free(name_without_realm.value);
896 if (name_token.value) {
897 GSS_LOCK_MUTEX(params->utils);
898 gss_release_buffer(&min_stat, &name_token);
899 GSS_UNLOCK_MUTEX(params->utils);
901 SETERROR(text->utils, "GSSAPI Failure");
902 sasl_gss_free_context_contents(text);
906 GSS_LOCK_MUTEX(params->utils);
907 maj_stat = gss_compare_name(&min_stat,
911 GSS_UNLOCK_MUTEX(params->utils);
913 if (GSS_ERROR(maj_stat)) {
914 params->utils->free(name_without_realm.value);
915 if (name_token.value) {
916 GSS_LOCK_MUTEX(params->utils);
917 gss_release_buffer(&min_stat, &name_token);
918 GSS_UNLOCK_MUTEX(params->utils);
921 GSS_LOCK_MUTEX(params->utils);
922 gss_release_name(&min_stat, &without);
923 GSS_UNLOCK_MUTEX(params->utils);
925 SETERROR(text->utils, "GSSAPI Failure");
926 sasl_gss_free_context_contents(text);
930 GSS_LOCK_MUTEX(params->utils);
931 gss_release_name(&min_stat,&without);
932 GSS_UNLOCK_MUTEX(params->utils);
939 text->authid = strdup(name_without_realm.value);
941 if (text->authid == NULL) {
942 MEMERROR(params->utils);
946 text->authid = strdup(name_token.value);
948 if (text->authid == NULL) {
949 MEMERROR(params->utils);
954 if (name_token.value) {
955 GSS_LOCK_MUTEX(params->utils);
956 gss_release_buffer(&min_stat, &name_token);
957 GSS_UNLOCK_MUTEX(params->utils);
959 if (name_without_realm.value) {
960 params->utils->free(name_without_realm.value);
963 /* we have to decide what sort of encryption/integrity/etc.,
965 if (params->props.max_ssf < params->external_ssf) {
968 text->limitssf = params->props.max_ssf - params->external_ssf;
970 if (params->props.min_ssf < params->external_ssf) {
971 text->requiressf = 0;
973 text->requiressf = params->props.min_ssf - params->external_ssf;
976 if (!text->rfc2222_gss) {
977 if (out_flags & GSS_C_CONF_FLAG)
979 else if (out_flags & GSS_C_INTEG_FLAG)
984 text->limitssf = 128; /* XXX */
989 /* build up our security properties token */
990 if (params->props.maxbufsize > 0xFFFFFF) {
991 /* make sure maxbufsize isn't too large */
992 /* maxbufsize = 0xFFFFFF */
993 sasldata[1] = sasldata[2] = sasldata[3] = 0xFF;
995 sasldata[1] = (params->props.maxbufsize >> 16) & 0xFF;
996 sasldata[2] = (params->props.maxbufsize >> 8) & 0xFF;
997 sasldata[3] = (params->props.maxbufsize >> 0) & 0xFF;
1000 if(text->requiressf != 0 && !params->props.maxbufsize) {
1001 params->utils->seterror(params->utils->conn, 0,
1002 "GSSAPI needs a security layer but one is forbidden");
1003 return SASL_TOOWEAK;
1006 if (text->requiressf == 0) {
1007 sasldata[0] |= 1; /* authentication */
1009 if (text->requiressf <= 1 && text->limitssf >= 1
1010 && params->props.maxbufsize) {
1013 if (text->requiressf <= K5_MAX_SSF && text->limitssf >= K5_MAX_SSF
1014 && params->props.maxbufsize) {
1018 real_input_token.value = (void *)sasldata;
1019 real_input_token.length = 4;
1021 GSS_LOCK_MUTEX(params->utils);
1022 maj_stat = gss_wrap(&min_stat,
1024 0, /* Just integrity checking here */
1029 GSS_UNLOCK_MUTEX(params->utils);
1031 if (GSS_ERROR(maj_stat)) {
1032 sasl_gss_seterror(text->utils, maj_stat, min_stat);
1033 if (output_token->value) {
1034 GSS_LOCK_MUTEX(params->utils);
1035 gss_release_buffer(&min_stat, output_token);
1036 GSS_UNLOCK_MUTEX(params->utils);
1038 sasl_gss_free_context_contents(text);
1044 *serveroutlen = output_token->length;
1045 if (output_token->value) {
1047 ret = _plug_buf_alloc(text->utils, &(text->out_buf),
1048 &(text->out_buf_len), *serveroutlen);
1049 if(ret != SASL_OK) {
1050 GSS_LOCK_MUTEX(params->utils);
1051 gss_release_buffer(&min_stat, output_token);
1052 GSS_UNLOCK_MUTEX(params->utils);
1055 memcpy(text->out_buf, output_token->value, *serveroutlen);
1056 *serverout = text->out_buf;
1059 GSS_LOCK_MUTEX(params->utils);
1060 gss_release_buffer(&min_stat, output_token);
1061 GSS_UNLOCK_MUTEX(params->utils);
1064 /* Wait for ssf request and authid */
1065 text->state = SASL_GSSAPI_STATE_SSFREQ;
1067 return SASL_CONTINUE;
1070 case SASL_GSSAPI_STATE_SSFREQ: {
1071 real_input_token.value = (void *)clientin;
1072 real_input_token.length = clientinlen;
1074 GSS_LOCK_MUTEX(params->utils);
1075 maj_stat = gss_unwrap(&min_stat,
1081 GSS_UNLOCK_MUTEX(params->utils);
1083 if (GSS_ERROR(maj_stat)) {
1084 sasl_gss_seterror(text->utils, maj_stat, min_stat);
1085 sasl_gss_free_context_contents(text);
1089 layerchoice = (int)(((char *)(output_token->value))[0]);
1092 if (layerchoice == 1 && text->requiressf == 0) { /* no encryption */
1093 oparams->encode = NULL;
1094 oparams->decode = NULL;
1095 oparams->mech_ssf = 0;
1096 } else if (layerchoice == 2 && text->requiressf <= 1 &&
1097 text->limitssf >= 1) { /* integrity */
1098 oparams->encode=&gssapi_integrity_encode;
1099 oparams->decode=&gssapi_decode;
1100 oparams->mech_ssf=1;
1101 } else if (layerchoice == 4 && text->requiressf <= K5_MAX_SSF &&
1102 text->limitssf >= K5_MAX_SSF) { /* privacy */
1103 oparams->encode = &gssapi_privacy_encode;
1104 oparams->decode = &gssapi_decode;
1105 /* FIX ME: Need to extract the proper value here */
1106 oparams->mech_ssf = K5_MAX_SSF;
1108 /* not a supported encryption layer */
1109 SETERROR(text->utils,
1110 "protocol violation: client requested invalid layer");
1111 /* Mark that we attempted negotiation */
1112 oparams->mech_ssf = 2;
1113 if (output_token->value) {
1114 GSS_LOCK_MUTEX(params->utils);
1115 gss_release_buffer(&min_stat, output_token);
1116 GSS_UNLOCK_MUTEX(params->utils);
1118 sasl_gss_free_context_contents(text);
1122 if (output_token->length == 4 || !text->rfc2222_gss) {
1126 ret = params->canon_user(params->utils->conn,
1128 0, /* strlen(text->authid) */
1129 SASL_CU_AUTHZID | SASL_CU_AUTHID,
1132 if (ret != SASL_OK) {
1133 sasl_gss_free_context_contents(text);
1136 } else if (output_token->length > 4) {
1139 ret = params->canon_user(params->utils->conn,
1140 ((char *) output_token->value) + 4,
1141 (output_token->length - 4) * sizeof(char),
1142 SASL_CU_AUTHZID, oparams);
1144 if (ret != SASL_OK) {
1145 sasl_gss_free_context_contents(text);
1149 ret = params->canon_user(params->utils->conn,
1151 0, /* strlen(text->authid) */
1152 SASL_CU_AUTHID, oparams);
1153 if (ret != SASL_OK) {
1154 sasl_gss_free_context_contents(text);
1158 SETERROR(text->utils,
1160 GSS_LOCK_MUTEX(params->utils);
1161 gss_release_buffer(&min_stat, output_token);
1162 GSS_UNLOCK_MUTEX(params->utils);
1163 sasl_gss_free_context_contents(text);
1167 /* No matter what, set the rest of the oparams */
1169 if (text->client_creds != GSS_C_NO_CREDENTIAL) {
1170 oparams->client_creds = &text->client_creds;
1173 oparams->client_creds = NULL;
1176 oparams->gss_peer_name = text->client_name;
1177 oparams->gss_local_name = text->server_name;
1179 if (text->rfc2222_gss) {
1180 oparams->maxoutbuf =
1181 (((unsigned char *) output_token->value)[1] << 16) |
1182 (((unsigned char *) output_token->value)[2] << 8) |
1183 (((unsigned char *) output_token->value)[3] << 0);
1185 oparams->maxoutbuf = 0xFFFFFF;
1188 if (oparams->mech_ssf) {
1189 maj_stat = gss_wrap_size_limit( &min_stat,
1193 (OM_uint32) oparams->maxoutbuf,
1196 if(max_input > oparams->maxoutbuf) {
1197 /* Heimdal appears to get this wrong */
1198 oparams->maxoutbuf -= (max_input - oparams->maxoutbuf);
1200 /* This code is actually correct */
1201 oparams->maxoutbuf = max_input;
1205 GSS_LOCK_MUTEX(params->utils);
1206 gss_release_buffer(&min_stat, output_token);
1207 GSS_UNLOCK_MUTEX(params->utils);
1209 text->state = SASL_GSSAPI_STATE_AUTHENTICATED;
1211 /* used by layers */
1212 _plug_decode_init(&text->decode_context, text->utils,
1213 (params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF :
1214 params->props.maxbufsize);
1216 oparams->doneflag = 1;
1222 params->utils->log(NULL, SASL_LOG_ERR,
1223 "Invalid GSSAPI server step %d\n", text->state);
1227 return SASL_FAIL; /* should never get here */
1230 static sasl_server_plug_t gssapi_server_plugins[] =
1233 "GSSAPI", /* mech_name */
1234 K5_MAX_SSF, /* max_ssf */
1235 SASL_SEC_NOPLAINTEXT
1237 | SASL_SEC_NOANONYMOUS
1238 | SASL_SEC_MUTUAL_AUTH /* security_flags */
1239 | SASL_SEC_PASS_CREDENTIALS,
1240 SASL_FEAT_WANT_CLIENT_FIRST
1241 | SASL_FEAT_ALLOWS_PROXY, /* features */
1242 NULL, /* glob_context */
1243 &gssapi_server_mech_new, /* mech_new */
1244 &gssapi_server_mech_step, /* mech_step */
1245 &gssapi_common_mech_dispose, /* mech_dispose */
1246 &gssapi_common_mech_free, /* mech_free */
1248 NULL, /* user_query */
1250 NULL, /* mech_avail */
1254 "GSS-SPNEGO", /* mech_name */
1255 K5_MAX_SSF, /* max_ssf */
1256 SASL_SEC_NOPLAINTEXT
1258 | SASL_SEC_NOANONYMOUS
1259 | SASL_SEC_MUTUAL_AUTH /* security_flags */
1260 | SASL_SEC_PASS_CREDENTIALS,
1261 SASL_FEAT_WANT_CLIENT_FIRST
1262 | SASL_FEAT_ALLOWS_PROXY, /* features */
1263 NULL, /* glob_context */
1264 &gss_spnego_server_mech_new, /* mech_new */
1265 &gssapi_server_mech_step, /* mech_step */
1266 &gssapi_common_mech_dispose, /* mech_dispose */
1267 &gssapi_common_mech_free, /* mech_free */
1269 NULL, /* user_query */
1271 NULL, /* mech_avail */
1276 int gssapiv2_server_plug_init(
1277 #ifndef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY
1278 const sasl_utils_t *utils __attribute__((unused)),
1280 const sasl_utils_t *utils,
1284 sasl_server_plug_t **pluglist,
1287 #ifdef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY
1288 const char *keytab = NULL;
1289 char keytab_path[1024];
1293 if (maxversion < SASL_SERVER_PLUG_VERSION) {
1294 return SASL_BADVERS;
1297 #ifdef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY
1298 /* unfortunately, we don't check for readability of keytab if it's
1299 the standard one, since we don't know where it is */
1301 /* FIXME: This code is broken */
1303 utils->getopt(utils->getopt_context, "GSSAPI", "keytab", &keytab, &rl);
1304 if (keytab != NULL) {
1305 if (access(keytab, R_OK) != 0) {
1306 utils->log(NULL, SASL_LOG_ERR,
1307 "Could not find keytab file: %s: %m",
1312 if(strlen(keytab) > 1024) {
1313 utils->log(NULL, SASL_LOG_ERR,
1314 "path to keytab is > 1024 characters");
1315 return SASL_BUFOVER;
1318 strncpy(keytab_path, keytab, 1024);
1320 gsskrb5_register_acceptor_identity(keytab_path);
1324 *out_version = SASL_SERVER_PLUG_VERSION;
1325 *pluglist = gssapi_server_plugins;
1326 *plugcount = sizeof(gssapi_server_plugins)/sizeof(gssapi_server_plugins[0]);
1328 #ifdef GSS_USE_MUTEXES
1330 gss_mutex = utils->mutex_alloc();
1340 /***************************** Client Section *****************************/
1342 static int _gssapi_client_mech_new(void *glob_context __attribute__((unused)),
1343 sasl_client_params_t *params,
1346 void **conn_context)
1350 /* holds state are in */
1351 text = sasl_gss_new_context(params->utils);
1353 MEMERROR(params->utils);
1357 text->state = SASL_GSSAPI_STATE_AUTHNEG;
1358 text->gss_ctx = GSS_C_NO_CONTEXT;
1359 text->client_name = GSS_C_NO_NAME;
1360 text->server_creds = GSS_C_NO_CREDENTIAL;
1361 text->client_creds = GSS_C_NO_CREDENTIAL;
1363 text->rfc2222_gss = rfc2222_gss;
1365 *conn_context = text;
1370 static int gssapi_client_mech_new(void *glob_context,
1371 sasl_client_params_t *params,
1372 void **conn_context)
1374 return _gssapi_client_mech_new(glob_context, params, &gss_krb5_mechanism_oid_desc,
1378 static int gss_spnego_client_mech_new(void *glob_context,
1379 sasl_client_params_t *params,
1380 void **conn_context)
1382 return _gssapi_client_mech_new(glob_context, params, &gss_spnego_mechanism_oid_desc,
1386 static int gssapi_client_mech_step(void *conn_context,
1387 sasl_client_params_t *params,
1388 const char *serverin,
1389 unsigned serverinlen,
1390 sasl_interact_t **prompt_need,
1391 const char **clientout,
1392 unsigned *clientoutlen,
1393 sasl_out_params_t *oparams)
1395 context_t *text = (context_t *)conn_context;
1396 gss_buffer_t input_token, output_token;
1397 gss_buffer_desc real_input_token, real_output_token;
1398 OM_uint32 maj_stat = 0, min_stat = 0;
1399 OM_uint32 max_input, out_flags;
1400 gss_buffer_desc name_token;
1401 int ret, serverhas = 0;
1402 OM_uint32 req_flags = 0, out_req_flags = 0;
1403 sasl_security_properties_t *secprops = &(params->props);
1405 input_token = &real_input_token;
1406 output_token = &real_output_token;
1407 output_token->value = NULL;
1408 input_token->value = NULL;
1409 input_token->length = 0;
1414 switch (text->state) {
1416 case SASL_GSSAPI_STATE_AUTHNEG:
1417 /* try to get the userid */
1418 if (text->user == NULL) {
1419 int user_result = SASL_OK;
1420 int pass_result = SASL_OK;
1422 user_result = _plug_get_userid(params->utils, &text->user,
1425 if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) {
1426 sasl_gss_free_context_contents(text);
1430 /* free prompts we got */
1431 if (prompt_need && *prompt_need) {
1432 params->utils->free(*prompt_need);
1433 *prompt_need = NULL;
1436 if (params->gss_creds == GSS_C_NO_CREDENTIAL) {
1437 unsigned int free_password = 0;
1438 sasl_secret_t *password = NULL;
1440 pass_result = _plug_get_password(params->utils, &password,
1441 &free_password, prompt_need);
1442 if (pass_result == SASL_OK) {
1443 gss_buffer_desc pwBuf;
1444 gss_buffer_desc nameBuf;
1445 gss_OID_set_desc mechs;
1447 nameBuf.length = strlen(text->user);
1448 nameBuf.value = (void *)text->user;
1449 pwBuf.length = password->len;
1450 pwBuf.value = password->data;
1452 mechs.elements = text->mech;
1454 GSS_LOCK_MUTEX(params->utils);
1455 maj_stat = gss_import_name(&min_stat, &nameBuf,
1456 GSS_C_NT_USER_NAME, &text->client_name);
1457 if (maj_stat == GSS_S_COMPLETE) {
1458 maj_stat = gss_acquire_cred_with_password(&min_stat,
1460 &pwBuf, GSS_C_INDEFINITE,
1461 &mechs, GSS_C_INITIATE,
1462 &text->client_creds,
1464 if (GSS_ERROR(maj_stat)) {
1465 if (free_password) _plug_free_secret(params->utils, &password);
1466 sasl_gss_seterror(text->utils, maj_stat, min_stat);
1467 GSS_UNLOCK_MUTEX(params->utils);
1468 sasl_gss_free_context_contents(text);
1472 GSS_UNLOCK_MUTEX(params->utils);
1473 if (free_password) _plug_free_secret(params->utils, &password);
1476 /* if there are prompts not filled in */
1477 if (user_result == SASL_INTERACT && text->mech != &gss_krb5_mechanism_oid_desc) {
1479 _plug_make_prompts(params->utils, prompt_need,
1480 user_result == SASL_INTERACT ?
1481 "Please enter your authorization name" : NULL,
1484 pass_result == SASL_INTERACT ?
1485 "Please enter your password" : NULL, NULL,
1488 if (result != SASL_OK) return result;
1489 return SASL_INTERACT;
1494 if (text->server_name == GSS_C_NO_NAME) { /* only once */
1495 name_token.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
1496 name_token.value = (char *)params->utils->malloc((name_token.length + 1) * sizeof(char));
1497 if (name_token.value == NULL) {
1498 sasl_gss_free_context_contents(text);
1501 if (params->serverFQDN == NULL
1502 || strlen(params->serverFQDN) == 0) {
1503 SETERROR(text->utils, "GSSAPI Failure: no serverFQDN");
1507 sprintf(name_token.value,"%s@%s", params->service, params->serverFQDN);
1509 GSS_LOCK_MUTEX(params->utils);
1510 maj_stat = gss_import_name (&min_stat,
1512 GSS_C_NT_HOSTBASED_SERVICE,
1513 &text->server_name);
1514 GSS_UNLOCK_MUTEX(params->utils);
1516 params->utils->free(name_token.value);
1517 name_token.value = NULL;
1519 if (GSS_ERROR(maj_stat)) {
1520 sasl_gss_seterror(text->utils, maj_stat, min_stat);
1521 sasl_gss_free_context_contents(text);
1526 if (serverinlen == 0)
1527 input_token = GSS_C_NO_BUFFER;
1530 real_input_token.value = (void *)serverin;
1531 real_input_token.length = serverinlen;
1533 else if (text->gss_ctx != GSS_C_NO_CONTEXT ) {
1534 /* This can't happen under GSSAPI: we have a non-null context
1535 * and no input from the server. However, thanks to Imap,
1536 * which discards our first output, this happens all the time.
1537 * Throw away the context and try again. */
1538 GSS_LOCK_MUTEX(params->utils);
1539 maj_stat = gss_delete_sec_context (&min_stat,&text->gss_ctx,GSS_C_NO_BUFFER);
1540 GSS_UNLOCK_MUTEX(params->utils);
1541 text->gss_ctx = GSS_C_NO_CONTEXT;
1544 /* Setup req_flags properly */
1545 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG;
1546 if(params->props.max_ssf > params->external_ssf) {
1547 /* We are requesting a security layer */
1548 req_flags |= GSS_C_INTEG_FLAG;
1549 /* Any SSF bigger than 1 is confidentiality. */
1550 /* Let's check if the client of the API requires confidentiality,
1551 and it wasn't already provided by an external layer */
1552 if(params->props.max_ssf - params->external_ssf > 1) {
1553 /* We want to try for privacy */
1554 req_flags |= GSS_C_CONF_FLAG;
1558 if (params->props.security_flags & SASL_SEC_PASS_CREDENTIALS)
1559 req_flags = req_flags | GSS_C_DELEG_FLAG;
1561 GSS_LOCK_MUTEX(params->utils);
1562 maj_stat = gss_init_sec_context(&min_stat,
1563 params->gss_creds ? params->gss_creds :
1570 GSS_C_NO_CHANNEL_BINDINGS,
1576 GSS_UNLOCK_MUTEX(params->utils);
1578 if (GSS_ERROR(maj_stat)) {
1579 sasl_gss_seterror(text->utils, maj_stat, min_stat);
1580 if (output_token->value) {
1581 GSS_LOCK_MUTEX(params->utils);
1582 gss_release_buffer(&min_stat, output_token);
1583 GSS_UNLOCK_MUTEX(params->utils);
1585 sasl_gss_free_context_contents(text);
1589 if ((out_req_flags & GSS_C_DELEG_FLAG) != (req_flags & GSS_C_DELEG_FLAG)) {
1590 text->utils->seterror(text->utils->conn, SASL_LOG_WARN, "GSSAPI warning: no credentials were passed");
1591 /* not a fatal error */
1594 *clientoutlen = output_token->length;
1596 if (output_token->value) {
1598 ret = _plug_buf_alloc(text->utils, &(text->out_buf),
1599 &(text->out_buf_len), *clientoutlen);
1600 if(ret != SASL_OK) {
1601 GSS_LOCK_MUTEX(params->utils);
1602 gss_release_buffer(&min_stat, output_token);
1603 GSS_UNLOCK_MUTEX(params->utils);
1606 memcpy(text->out_buf, output_token->value, *clientoutlen);
1607 *clientout = text->out_buf;
1610 GSS_LOCK_MUTEX(params->utils);
1611 gss_release_buffer(&min_stat, output_token);
1612 GSS_UNLOCK_MUTEX(params->utils);
1615 if (maj_stat == GSS_S_COMPLETE) {
1616 GSS_LOCK_MUTEX(params->utils);
1617 if (text->client_name != GSS_C_NO_NAME)
1618 gss_release_name(&min_stat, &text->client_name);
1619 maj_stat = gss_inquire_context(&min_stat,
1622 NULL, /* targ_name */
1623 NULL, /* lifetime */
1625 /* FIX ME: Should check the resulting flags here */
1626 &out_flags, /* flags */
1627 NULL, /* local init */
1629 GSS_UNLOCK_MUTEX(params->utils);
1631 if (GSS_ERROR(maj_stat)) {
1632 sasl_gss_seterror(text->utils, maj_stat, min_stat);
1633 sasl_gss_free_context_contents(text);
1637 name_token.length = 0;
1638 GSS_LOCK_MUTEX(params->utils);
1639 maj_stat = gss_display_name(&min_stat,
1643 GSS_UNLOCK_MUTEX(params->utils);
1645 if (GSS_ERROR(maj_stat)) {
1646 if (name_token.value) {
1647 GSS_LOCK_MUTEX(params->utils);
1648 gss_release_buffer(&min_stat, &name_token);
1649 GSS_UNLOCK_MUTEX(params->utils);
1651 SETERROR(text->utils, "GSSAPI Failure");
1652 sasl_gss_free_context_contents(text);
1656 if (text->user && text->user[0]) {
1657 ret = params->canon_user(params->utils->conn,
1659 SASL_CU_AUTHZID, oparams);
1661 ret = params->canon_user(params->utils->conn,
1662 name_token.value, 0,
1663 SASL_CU_AUTHID, oparams);
1665 ret = params->canon_user(params->utils->conn,
1666 name_token.value, 0,
1667 SASL_CU_AUTHID | SASL_CU_AUTHZID,
1670 GSS_LOCK_MUTEX(params->utils);
1671 gss_release_buffer(&min_stat, &name_token);
1672 GSS_UNLOCK_MUTEX(params->utils);
1674 oparams->gss_peer_name = text->server_name;
1675 oparams->gss_local_name = text->client_name;
1677 if (ret != SASL_OK) return ret;
1679 if (text->rfc2222_gss) {
1680 /* Switch to ssf negotiation */
1681 text->state = SASL_GSSAPI_STATE_SSFCAP;
1682 return SASL_CONTINUE;
1685 if (out_flags & GSS_C_INTEG_FLAG)
1687 if (out_flags & GSS_C_CONF_FLAG)
1692 return SASL_CONTINUE;
1695 case SASL_GSSAPI_STATE_SSFCAP: {
1696 unsigned int alen, external = params->external_ssf;
1697 sasl_ssf_t need, allowed;
1700 real_input_token.value = (void *) serverin;
1701 real_input_token.length = serverinlen;
1703 GSS_LOCK_MUTEX(params->utils);
1704 maj_stat = gss_unwrap(&min_stat,
1710 GSS_UNLOCK_MUTEX(params->utils);
1712 if (GSS_ERROR(maj_stat)) {
1713 sasl_gss_seterror(text->utils, maj_stat, min_stat);
1714 sasl_gss_free_context_contents(text);
1715 if (output_token->value) {
1716 GSS_LOCK_MUTEX(params->utils);
1717 gss_release_buffer(&min_stat, output_token);
1718 GSS_UNLOCK_MUTEX(params->utils);
1723 /* bit mask of server support */
1724 serverhas = ((char *)output_token->value)[0];
1727 /* taken from kerberos.c */
1728 if (secprops->min_ssf > (K5_MAX_SSF + external)) {
1729 return SASL_TOOWEAK;
1730 } else if (secprops->min_ssf > secprops->max_ssf) {
1731 return SASL_BADPARAM;
1734 /* need bits of layer -- sasl_ssf_t is unsigned so be careful */
1735 if (secprops->max_ssf >= external) {
1736 allowed = secprops->max_ssf - external;
1740 if (secprops->min_ssf >= external) {
1741 need = secprops->min_ssf - external;
1747 /* if client didn't set use strongest layer available */
1748 if (allowed >= K5_MAX_SSF && need <= K5_MAX_SSF && (serverhas & 4)) {
1750 oparams->encode = &gssapi_privacy_encode;
1751 oparams->decode = &gssapi_decode;
1752 /* FIX ME: Need to extract the proper value here */
1753 oparams->mech_ssf = K5_MAX_SSF;
1755 } else if (allowed >= 1 && need <= 1 && (serverhas & 2)) {
1757 oparams->encode = &gssapi_integrity_encode;
1758 oparams->decode = &gssapi_decode;
1759 oparams->mech_ssf = 1;
1761 } else if (need <= 0 && (serverhas & 1)) {
1763 oparams->encode = NULL;
1764 oparams->decode = NULL;
1765 oparams->mech_ssf = 0;
1768 /* there's no appropriate layering for us! */
1769 sasl_gss_free_context_contents(text);
1770 return SASL_TOOWEAK;
1773 if (text->rfc2222_gss) {
1774 oparams->maxoutbuf =
1775 (((unsigned char *) output_token->value)[1] << 16) |
1776 (((unsigned char *) output_token->value)[2] << 8) |
1777 (((unsigned char *) output_token->value)[3] << 0);
1779 oparams->maxoutbuf = 0xFFFFFF;
1782 if(oparams->mech_ssf) {
1783 maj_stat = gss_wrap_size_limit( &min_stat,
1787 (OM_uint32) oparams->maxoutbuf,
1790 if(max_input > oparams->maxoutbuf) {
1791 /* Heimdal appears to get this wrong */
1792 oparams->maxoutbuf -= (max_input - oparams->maxoutbuf);
1794 /* This code is actually correct */
1795 oparams->maxoutbuf = max_input;
1799 GSS_LOCK_MUTEX(params->utils);
1800 gss_release_buffer(&min_stat, output_token);
1801 GSS_UNLOCK_MUTEX(params->utils);
1803 if (!text->rfc2222_gss) {
1804 text->state = SASL_GSSAPI_STATE_AUTHENTICATED;
1806 oparams->doneflag = 1;
1808 /* used by layers */
1809 _plug_decode_init(&text->decode_context, text->utils,
1810 (params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF :
1811 params->props.maxbufsize);
1815 /* oparams->user is always set, due to canon_user requirements.
1816 * Make sure the client actually requested it though, by checking
1817 * if our context was set.
1819 if (text->user && text->user[0])
1820 alen = strlen(oparams->user);
1824 input_token->length = 4 + alen;
1825 input_token->value =
1826 (char *)params->utils->malloc((input_token->length + 1)*sizeof(char));
1827 if (input_token->value == NULL) {
1828 sasl_gss_free_context_contents(text);
1833 memcpy((char *)input_token->value+4,oparams->user,alen);
1835 /* build up our security properties token */
1836 if (params->props.maxbufsize > 0xFFFFFF) {
1837 /* make sure maxbufsize isn't too large */
1838 /* maxbufsize = 0xFFFFFF */
1839 ((unsigned char *)input_token->value)[1] = 0xFF;
1840 ((unsigned char *)input_token->value)[2] = 0xFF;
1841 ((unsigned char *)input_token->value)[3] = 0xFF;
1843 ((unsigned char *)input_token->value)[1] =
1844 (params->props.maxbufsize >> 16) & 0xFF;
1845 ((unsigned char *)input_token->value)[2] =
1846 (params->props.maxbufsize >> 8) & 0xFF;
1847 ((unsigned char *)input_token->value)[3] =
1848 (params->props.maxbufsize >> 0) & 0xFF;
1850 ((unsigned char *)input_token->value)[0] = mychoice;
1852 GSS_LOCK_MUTEX(params->utils);
1853 maj_stat = gss_wrap (&min_stat,
1855 0, /* Just integrity checking here */
1860 GSS_UNLOCK_MUTEX(params->utils);
1862 params->utils->free(input_token->value);
1863 input_token->value = NULL;
1865 if (GSS_ERROR(maj_stat)) {
1866 sasl_gss_seterror(text->utils, maj_stat, min_stat);
1867 if (output_token->value) {
1868 GSS_LOCK_MUTEX(params->utils);
1869 gss_release_buffer(&min_stat, output_token);
1870 GSS_UNLOCK_MUTEX(params->utils);
1872 sasl_gss_free_context_contents(text);
1877 *clientoutlen = output_token->length;
1878 if (output_token->value) {
1880 ret = _plug_buf_alloc(text->utils, &(text->out_buf),
1881 &(text->out_buf_len), *clientoutlen);
1882 if (ret != SASL_OK) {
1883 GSS_LOCK_MUTEX(params->utils);
1884 gss_release_buffer(&min_stat, output_token);
1885 GSS_UNLOCK_MUTEX(params->utils);
1888 memcpy(text->out_buf, output_token->value, *clientoutlen);
1889 *clientout = text->out_buf;
1892 GSS_LOCK_MUTEX(params->utils);
1893 gss_release_buffer(&min_stat, output_token);
1894 GSS_UNLOCK_MUTEX(params->utils);
1898 text->state = SASL_GSSAPI_STATE_AUTHENTICATED;
1900 oparams->doneflag = 1;
1902 /* used by layers */
1903 _plug_decode_init(&text->decode_context, text->utils,
1904 (params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF :
1905 params->props.maxbufsize);
1911 params->utils->log(NULL, SASL_LOG_ERR,
1912 "Invalid GSSAPI client step %d\n", text->state);
1916 return SASL_FAIL; /* should never get here */
1919 static const unsigned long gssapi_required_prompts[] = {
1923 static sasl_client_plug_t gssapi_client_plugins[] =
1926 "GSSAPI", /* mech_name */
1927 K5_MAX_SSF, /* max_ssf */
1928 SASL_SEC_NOPLAINTEXT
1930 | SASL_SEC_NOANONYMOUS
1931 | SASL_SEC_MUTUAL_AUTH
1932 | SASL_SEC_PASS_CREDENTIALS, /* security_flags */
1933 SASL_FEAT_NEEDSERVERFQDN
1934 | SASL_FEAT_WANT_CLIENT_FIRST
1935 | SASL_FEAT_ALLOWS_PROXY, /* features */
1936 gssapi_required_prompts, /* required_prompts */
1937 NULL, /* glob_context */
1938 &gssapi_client_mech_new, /* mech_new */
1939 &gssapi_client_mech_step, /* mech_step */
1940 &gssapi_common_mech_dispose, /* mech_dispose */
1941 &gssapi_common_mech_free, /* mech_free */
1947 "GSS-SPNEGO", /* mech_name */
1948 K5_MAX_SSF, /* max_ssf */
1949 SASL_SEC_NOPLAINTEXT
1951 | SASL_SEC_NOANONYMOUS
1952 | SASL_SEC_MUTUAL_AUTH
1953 | SASL_SEC_PASS_CREDENTIALS, /* security_flags */
1954 SASL_FEAT_NEEDSERVERFQDN
1955 | SASL_FEAT_WANT_CLIENT_FIRST
1956 | SASL_FEAT_ALLOWS_PROXY, /* features */
1957 gssapi_required_prompts, /* required_prompts */
1958 NULL, /* glob_context */
1959 &gss_spnego_client_mech_new, /* mech_new */
1960 &gssapi_client_mech_step, /* mech_step */
1961 &gssapi_common_mech_dispose, /* mech_dispose */
1962 &gssapi_common_mech_free, /* mech_free */
1969 int gssapiv2_client_plug_init(const sasl_utils_t *utils __attribute__((unused)),
1972 sasl_client_plug_t **pluglist,
1975 if (maxversion < SASL_CLIENT_PLUG_VERSION) {
1976 SETERROR(utils, "Version mismatch in GSSAPI");
1977 return SASL_BADVERS;
1980 *out_version = SASL_CLIENT_PLUG_VERSION;
1981 *pluglist = gssapi_client_plugins;
1982 *plugcount = sizeof(gssapi_client_plugins)/sizeof(gssapi_client_plugins[0]);
1984 #ifdef GSS_USE_MUTEXES
1986 gss_mutex = utils->mutex_alloc();