2 * Copyright (C) 1998-2000 Luke Howard. All rights reserved.
6 * Implementation of GSS-API client side binding for SASL
14 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <netinet/in.h>
21 #include <arpa/nameser.h>
26 #include "gssldap-int.h"
29 typedef gss_uint32 OM_uint32;
35 #define LDAP_SERVICE "_ldap"
36 #define TCP_PROTOCOL "_tcp"
38 int locate_ldap_server(char *domain, char **server_name);
39 int ldap_delete_tickets(LDAP *ld, char *service_name);
40 static int negotiate_security_options(gssldap_client_state_t state,
42 int ldap_reset_principal(LDAP *ld, char *service_name, gss_name_t target_name,
43 char *ldap_realm_name);
44 unsigned long gsssasl_pack_security_token(OM_uint32 *min_stat, gss_ctx_id_t context,
45 gsssasl_security_negotiation_t inmask,
46 size_t masklength, gss_buffer_t wrapped_tok);
48 int gsssasl_unpack_security_token(OM_uint32 *min_stat, gss_ctx_id_t context,
49 gss_buffer_t wrapped_tok,
50 gsssasl_security_negotiation_t *outmask,
54 static int debug_ = 1;
55 #define TRACE(x) do { if (debug_) fprintf(stderr, "%s\n", x); fflush(stderr); } while (0);
56 #define LDAP_PERROR(ld, x) do { if (debug_) ldap_perror(ld, x); } while (0);
57 #define LOG_STATUS(msg, min, maj) log_status(msg, min, maj)
60 #define LDAP_PERROR(ld, x)
61 #define LOG_STATUS(msg, min, maj)
62 #endif /* GSSSASL_DEBUG */
64 #ifdef HACK_SERVICE_NAME
65 char *__service = NULL;
69 * The read and write fns need to access the context in here and
70 * there doesn't appear to be a way to pass this info to them.
72 static gss_ctx_id_t security_context;
73 static int security_layer = 0;
74 static unsigned long security_token_size = 0;
76 LDAP_CALLBACK int LDAP_C ldap_gssapi_read(LBER_SOCKET sock, void *data,
83 gss_buffer_desc recv_tok;
84 gss_buffer_desc wrapped_tok;
88 (GSSSASL_INTEGRITY_PROTECTION | GSSSASL_PRIVACY_PROTECTION))
90 if (recv(sock, (char *)&pdulen, sizeof(pdulen), 0) != sizeof(pdulen))
92 wrapped_tok.length = ntohl(pdulen);
96 fprintf(stderr, "Reading data of %d octets\n",
99 #endif /* GSSSASL_DEBUG */
100 if ((int)wrapped_tok.length > security_token_size)
102 wrapped_tok.value = malloc(wrapped_tok.length);
103 if (wrapped_tok.value == NULL)
105 count = recv(sock, wrapped_tok.value, wrapped_tok.length, 0);
106 if (count != (int)wrapped_tok.length)
108 gss_release_buffer(&rc, &wrapped_tok);
111 maj_stat = gss_unwrap(&min_stat,
117 if (maj_stat != GSS_S_COMPLETE)
119 gss_release_buffer(&rc, &wrapped_tok);
125 fprintf(stderr, "Got %d bytes of %s data\n",
126 recv_tok.length, rc ? "private" : "signed");
128 #endif /* GSSSASL_DEBUG */
129 if ((int)recv_tok.length > len)
131 gss_release_buffer(&rc, &recv_tok);
132 gss_release_buffer(&rc, &wrapped_tok);
133 return GSS_S_FAILURE;
135 memcpy(data, recv_tok.value, recv_tok.length);
136 pdulen = recv_tok.length;
137 gss_release_buffer(&rc, &recv_tok);
138 gss_release_buffer(&rc, &wrapped_tok);
143 return read(sock, data, len);
146 LDAP_CALLBACK int LDAP_C ldap_gssapi_write(LBER_SOCKET sock, const void *data,
153 gss_buffer_desc send_tok;
154 gss_buffer_desc wrapped_tok;
158 (GSSSASL_INTEGRITY_PROTECTION | GSSSASL_PRIVACY_PROTECTION))
160 send_tok.length = len;
161 send_tok.value = (void *) data;
165 fprintf(stderr, "Sending %d bytes of data\n",
168 #endif /* GSSSASL_DEBUG */
169 maj_stat = gss_wrap(&min_stat,
171 (security_layer & GSSSASL_PRIVACY_PROTECTION) ? 1 : 0,
176 if (maj_stat != GSS_S_COMPLETE)
181 fprintf(stderr, "Sent %d bytes of %s data\n",
182 wrapped_tok.length, rc ? "private" : "signed");
184 #endif /* GSSSASL_DEBUG */
185 pdulen = htonl(wrapped_tok.length);
186 ptr = calloc(1, sizeof(pdulen) + wrapped_tok.length);
187 memcpy(ptr, &pdulen, sizeof(pdulen));
188 memcpy(&ptr[sizeof(pdulen)], wrapped_tok.value, wrapped_tok.length);
189 rc = send(sock, ptr, sizeof(pdulen) + wrapped_tok.length, 0);
191 if (rc == (wrapped_tok.length + sizeof(pdulen)))
193 gss_release_buffer(&maj_stat, &wrapped_tok);
197 return send(sock, data, len, 0);
202 * Dump the token to stderr
204 static void print_token(gss_buffer_t tok)
207 unsigned char *p = tok->value;
209 for (i = 0; i < (int)tok->length; i++, p++)
211 fprintf(stderr, "%02x ", *p);
214 fprintf(stderr, "\n");
217 fprintf(stderr, "\n");
223 * Handle a GSS-API error
225 static void log_status_impl(const char *reason, OM_uint32 res, int type)
227 OM_uint32 maj_stat, min_stat;
234 maj_stat = gss_display_status(&min_stat,
243 fprintf(stderr, "ldap_adgssapi_bind: %s: %s\n",
247 (void) gss_release_buffer(&min_stat, &msg);
255 * Cover function to handle a GSS-API error
257 static void log_status(const char *reason, OM_uint32 maj_stat,
260 log_status_impl(reason, maj_stat, GSS_C_GSS_CODE);
261 log_status_impl(reason, min_stat, GSS_C_MECH_CODE);
264 #endif /* GSSSASL_DEBUG */
267 * Send a GSS-API token as part of a SASL BindRequest
269 static int send_token(gssldap_client_state_t state, gss_buffer_t send_tok)
273 TRACE("==> send_token");
275 cred.bv_val = send_tok->value;
276 cred.bv_len = send_tok->length;
278 if (ldap_sasl_bind(state->ld,
284 &state->msgid) != LDAP_SUCCESS)
286 LDAP_PERROR(state->ld, "send_token");
287 TRACE("<== send_token");
290 TRACE("<== send_token");
295 * Parse the final result sent back from the server.
297 static int parse_bind_result(gssldap_client_state_t state)
299 LDAPMessage *res = NULL, *msg = NULL;
302 TRACE("==> parse_bind_result");
304 if (ldap_result(state->ld,
310 LDAP_PERROR(state->ld, "ldap_result");
311 TRACE("<== parse_bind_result");
314 for (msg = ldap_first_message(state->ld, res);
316 msg = ldap_next_message(state->ld, msg))
318 if (ldap_msgtype(msg) == LDAP_RES_BIND)
320 ldap_parse_result(state->ld, msg, &rc, NULL, NULL, NULL, NULL, 0);
328 TRACE("<== parse_bind_result");
330 if (rc == LDAP_SUCCESS)
339 * Receive a GSS-API token from a SASL BindResponse
340 * The contents of recv_tok must be freed by the
343 static int recv_token(gssldap_client_state_t state, gss_buffer_t recv_tok)
345 struct berval *servercred = NULL;
346 LDAPMessage *res = NULL, *msg = NULL;
349 TRACE("==> recv_token");
351 if (ldap_result(state->ld, state->msgid, LDAP_MSG_ALL, NULL, &res) <= 0)
353 LDAP_PERROR(state->ld, "ldap_result");
354 TRACE("<== recv_token");
357 recv_tok->value = NULL;
358 recv_tok->length = 0;
360 for (msg = ldap_first_message(state->ld, res);
362 msg = ldap_next_message(state->ld, msg))
364 if (ldap_msgtype(msg) == LDAP_RES_BIND && servercred == NULL)
366 rc = ldap_parse_sasl_bind_result(state->ld,
370 if (rc == LDAP_SUCCESS && servercred != NULL)
372 recv_tok->value = malloc(servercred->bv_len);
373 if (recv_tok->value != NULL)
375 memcpy(recv_tok->value, servercred->bv_val, servercred->bv_len);
376 recv_tok->length = servercred->bv_len;
389 TRACE("<== recv_token");
390 if (servercred != NULL)
392 nslberi_free(servercred);
393 TRACE("<== recv_token");
396 if (state->rc == LDAP_SUCCESS)
398 state->rc = LDAP_OPERATIONS_ERROR;
402 LDAP_PERROR(state->ld, "recv_token");
405 TRACE("<== recv_token");
410 * The client calls GSS_Init_sec_context, passing in 0 for
411 * input_context_handle (initially) and a targ_name equal to output_name
412 * from GSS_Import_Name called with input_name_type of
413 * GSS_C_NT_HOSTBASED_SERVICE and input_name_string of
414 * "service@hostname" where "service" is the service name specified in
415 * the protocol's profile, and "hostname" is the fully qualified host
416 * name of the server. The client then responds with the resulting
417 * output_token. If GSS_Init_sec_context returns GSS_S_CONTINUE_NEEDED,
418 * then the client should expect the server to issue a token in a
419 * subsequent challenge. The client must pass the token to another call
420 * to GSS_Init_sec_context, repeating the actions in this paragraph.
422 static int client_establish_context(LDAP *ld, gssldap_client_state_t state,
423 char *service_name, char *ldap_realm_name)
425 gss_buffer_desc send_tok;
426 gss_buffer_desc recv_tok;
427 gss_buffer_desc *token_ptr;
428 gss_name_t target_name;
431 gss_OID oid = GSS_C_NULL_OID;
434 TRACE("==> client_establish_context");
436 memset(&recv_tok, '\0', sizeof(recv_tok));
437 send_tok.value = service_name;
438 send_tok.length = strlen(send_tok.value) + 1;
440 maj_stat = gss_import_name(&min_stat,
442 (gss_OID) gss_nt_service_name,
446 if (ldap_reset_principal(ld, service_name, target_name, ldap_realm_name))
448 TRACE("<== client_establish_context");
452 token_ptr = GSS_C_NO_BUFFER;
453 state->context = GSS_C_NO_CONTEXT;
457 maj_stat = gss_init_sec_context(&min_stat,
462 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
471 if (token_ptr != GSS_C_NO_BUFFER)
472 (void) gss_release_buffer(&min_stat, &recv_tok);
474 if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
476 LOG_STATUS("initializing context", maj_stat, min_stat);
477 (void) gss_release_name(&min_stat, &target_name);
478 TRACE("<== client_establish_context");
484 fprintf(stderr, "Sending init_sec_context token (size=%d)...\n",
487 print_token(&send_tok);
490 if (send_token(state, &send_tok) < 0)
492 TRACE("Send_token failed");
493 (void) gss_release_buffer(&min_stat, &send_tok);
494 (void) gss_release_name(&min_stat, &target_name);
495 TRACE("<== client_establish_context");
498 (void) gss_release_buffer(&min_stat, &send_tok);
499 if (maj_stat == GSS_S_CONTINUE_NEEDED)
501 TRACE("continue needed...");
502 if (recv_token(state, &recv_tok) < 0)
504 TRACE("recv_token failed");
505 (void) gss_release_name(&min_stat, &target_name);
506 TRACE("<== client_establish_context");
512 fprintf(stderr, "Received token (size=%d)...\n",
515 print_token(&recv_tok);
518 token_ptr = &recv_tok;
520 } while (maj_stat == GSS_S_CONTINUE_NEEDED);
522 (void) gss_release_name(&min_stat, &target_name);
524 TRACE("<== client_establish_context");
529 * When GSS_Init_sec_context returns GSS_S_COMPLETE, the client takes
530 * the following actions: If the last call to GSS_Init_sec_context
531 * returned an output_token, then the client responds with the
532 * output_token, otherwise the client responds with no data. The client
533 * should then expect the server to issue a token in a subsequent
534 * challenge. The client passes this token to GSS_Unwrap and interprets
535 * the first octet of resulting cleartext as a bit-mask specifying the
536 * security layers supported by the server and the second through fourth
537 * octets as the maximum size output_message to send to the server. The
538 * client then constructs data, with the first octet containing the
539 * bit-mask specifying the selected security layer, the second through
540 * fourth octets containing in network byte order the maximum size
541 * output_message the client is able to receive, and the remaining
542 * octets containing the authorization identity. The client passes the
543 * data to GSS_Wrap with conf_flag set to FALSE, and responds with the
544 * generated output_message. The client can then consider the server
547 static int negotiate_security_options(gssldap_client_state_t state,
552 gss_buffer_desc recv_tok;
553 gss_buffer_desc send_tok;
557 gsssasl_security_negotiation_t send_mask;
558 gsssasl_security_negotiation_t recv_mask;
560 TRACE("==> negotiate_security_options");
562 memset(&send_tok, '\0', sizeof(send_tok));
563 memset(&recv_tok, '\0', sizeof(recv_tok));
564 if ((rc = recv_token(state, &recv_tok)) < 0)
566 TRACE("<== negotiate_security_options (recv_token failed)");
572 fprintf(stderr, "Received token (size=%d)...\n",
575 print_token(&recv_tok);
577 #endif /* GSSSASL_DEBUG */
579 maj_stat = gsssasl_unpack_security_token(&min_stat,
585 if (maj_stat != GSS_S_COMPLETE)
587 LOG_STATUS("unpacking security negotiation token",
590 TRACE("<== negotiate_security_options (unpack failed)");
596 fprintf(stderr, "Received security token level %d size %d\n",
597 recv_mask->security_layer,
598 recv_mask->token_size);
601 if ((~recv_mask->security_layer) & layer)
604 TRACE("<== negotiate_security_options (unsupported security layer)");
607 mask_length = sizeof(recv_mask);
608 send_mask = NSLDAPI_MALLOC(mask_length);
609 if (send_mask == NULL)
612 TRACE("<== negotiate_security_options (malloc failed)");
615 memset(send_mask, '\0', mask_length);
616 send_mask->security_layer = layer;
617 send_mask->token_size = recv_mask->token_size;
618 memcpy(send_mask->identity, "", strlen(""));
624 fprintf(stderr, "Sending security token level %d size %d\n",
625 send_mask->security_layer,
626 send_mask->token_size);
629 maj_stat = gsssasl_pack_security_token(&min_stat,
634 if (maj_stat != GSS_S_COMPLETE)
636 LOG_STATUS("packing security negotiation token", maj_stat, min_stat);
637 NSLDAPI_FREE(send_mask);
638 TRACE("<== negotiate_security_options (pack failed)");
641 if ((rc = send_token(state, &send_tok)) < 0)
643 NSLDAPI_FREE(send_mask);
644 TRACE("<== negotiate_security_options (send_token failed)");
647 rc = parse_bind_result(state);
652 security_context = state->context;
653 security_layer = layer;
654 security_token_size = ntohl(send_mask->token_size);
656 security_token_size >>= 8;
660 NSLDAPI_FREE(send_mask);
661 if (send_tok.value != NULL)
662 gss_release_buffer(&rc, &send_tok);
663 if (recv_tok.value != NULL)
664 gss_release_buffer(&rc, &recv_tok);
665 TRACE("<== negotiate_security_options");
671 * Temporary function to enable debugging
674 LDAP_CALL ldap_gssapi_debug(int on)
680 #endif /* GSSSASL_DEBUG */
683 * Public function for doing a GSS-API SASL bind
686 LDAP_CALL ldap_adgssapi_bind(LDAP *ld, const char *who, int layer,
687 char *ldap_domain_name,
688 char *ldap_realm_name, char *server)
690 gssldap_client_state_desc state;
695 struct ldap_io_fns iofns;
698 if (!NSLDAPI_VALID_LDAP_POINTER(ld) || ld->ld_defhost == NULL)
703 for (i = 0; i < (int)strlen(ld->ld_defhost); i++)
704 ld->ld_defhost[i] = toupper(ld->ld_defhost[i]);
707 NSLDAPI_MALLOC(sizeof(GSSAPI_LDAP_SERVICE_NAME "@") +
709 if (service_name == NULL)
713 strcpy(service_name, GSSAPI_LDAP_SERVICE_NAME "@");
714 strcat(service_name, lowercase(server));
720 fprintf(stderr, "LDAP service name: %s\n", service_name);
728 state.context = GSS_C_NO_CONTEXT;
729 state.rc = LDAP_OPERATIONS_ERROR;
731 rc = client_establish_context(ld, &state, service_name, ldap_realm_name);
734 rc = negotiate_security_options(&state, layer);
739 gss_buffer_desc tname;
740 gss_buffer_desc sname;
741 gss_name_t targ_name;
743 OM_uint32 context_flags;
751 maj_stat = gss_inquire_context(&min_stat,
760 if (maj_stat != GSS_S_COMPLETE)
762 LOG_STATUS("inquiring context", maj_stat, min_stat);
763 return LDAP_OPERATIONS_ERROR;
765 maj_stat = gss_display_name(&min_stat,
769 if (maj_stat != GSS_S_COMPLETE)
771 LOG_STATUS("displaying source name", maj_stat, min_stat);
772 return LDAP_OPERATIONS_ERROR;
774 maj_stat = gss_display_name(&min_stat,
778 if (maj_stat != GSS_S_COMPLETE)
780 LOG_STATUS("displaying target name", maj_stat, min_stat);
781 return LDAP_OPERATIONS_ERROR;
786 "\"%.*s\" to \"%.*s\", lifetime %d, flags %x, %s, %s\n",
787 (int) sname.length, (char *) sname.value,
788 (int) tname.length, (char *) tname.value, lifetime,
790 (is_local) ? "locally initiated" : "remotely initiated",
791 (is_open) ? "open" : "closed");
794 (void) gss_release_name(&min_stat, &src_name);
795 (void) gss_release_name(&min_stat, &targ_name);
796 (void) gss_release_buffer(&min_stat, &sname);
797 (void) gss_release_buffer(&min_stat, &tname);
799 state.rc = LDAP_SUCCESS;
802 if (state.rc == LDAP_SUCCESS)
804 if (layer == GSSSASL_PRIVACY_PROTECTION)
806 memset(&iofns, 0, sizeof(iofns));
807 iofns.liof_read = ldap_gssapi_read;
808 iofns.liof_write = ldap_gssapi_write;
809 state.rc = ldap_set_option(ld, LDAP_OPT_IO_FN_PTRS, &iofns);
813 NSLDAPI_FREE(service_name);
814 LDAP_SET_LDERRNO(ld, state.rc, NULL, NULL);
819 /* Wrap and encode a security negotiation token */
820 unsigned long gsssasl_pack_security_token(OM_uint32 *min_stat,
821 gss_ctx_id_t context,
822 gsssasl_security_negotiation_t inmask,
823 size_t masklength, gss_buffer_t wrapped_tok)
827 gss_buffer_desc send_tok;
829 wrapped_tok->length = 0;
830 wrapped_tok->value = NULL;
833 if (masklength < sizeof(gsssasl_security_negotiation_desc))
834 return GSS_S_FAILURE;
837 inmask->token_size = inmask->token_size;
839 send_tok.length = masklength;
840 send_tok.value = inmask;
842 maj_stat = gss_wrap(min_stat,
850 /* inmask->token_size = ntohl(inmask->token_size);*/
855 /* Unwrap and decode a security negotiation token. */
856 int gsssasl_unpack_security_token(OM_uint32 *min_stat, gss_ctx_id_t context,
857 gss_buffer_t wrapped_tok,
858 gsssasl_security_negotiation_t *outmask,
863 gss_buffer_desc recv_tok;
867 memset(&recv_tok, '\0', sizeof(recv_tok));
869 maj_stat = gss_unwrap(min_stat,
875 if (maj_stat != GSS_S_COMPLETE)
880 if (recv_tok.length < sizeof(gsssasl_security_negotiation_desc))
882 gss_release_buffer(&rc, &recv_tok);
883 return GSS_S_FAILURE;
889 * we're lazy and don't copy the token. This could cause
890 * problems if libgssapi uses a different malloc!
892 *masklength = recv_tok.length;
893 *outmask = (gsssasl_security_negotiation_t) recv_tok.value;
894 if (*outmask == NULL)
896 gss_release_buffer(&rc, &recv_tok);
897 return GSS_S_FAILURE;
899 return GSS_S_COMPLETE;
902 int ldap_reset_principal(LDAP *ld, char *service_name, gss_name_t target_name,
903 char *ldap_realm_name)
905 krb5_context context = NULL;
906 krb5_principal princ;
907 krb5_principal princ1;
908 krb5_principal temp_princ;
910 char *server_name = NULL;
913 princ = (krb5_principal)(target_name);
915 if (krb5_init_context(&context))
918 realm = strdup(ldap_realm_name);
919 for (i = 0; i < (int)strlen(realm); i++)
920 realm[i] = toupper(realm[i]);
921 server_name = strdup(ld->ld_defhost);
922 for (i = 0; i < (int)strlen(server_name); i++)
923 server_name[i] = tolower(server_name[i]);
925 temp_princ = malloc(sizeof(*princ));
926 memcpy(temp_princ, princ, sizeof(*princ));
928 krb5_build_principal(context, &princ1, strlen(realm), realm, "ldap",
931 memcpy(princ, princ1, sizeof(*princ1));
933 krb5_free_principal(context, temp_princ);
937 if (server_name != NULL)
940 krb5_free_context(context);
944 int ldap_delete_tickets(LDAP *ld, char *service_name)
948 krb5_context context = NULL;
949 krb5_ccache v5Cache = NULL;
951 krb5_cc_cursor v5Cursor;
952 krb5_error_code code;
955 if (!NSLDAPI_VALID_LDAP_POINTER(ld) || ld->ld_defhost == NULL)
960 for (i = 0; i < (int)strlen(ld->ld_defhost); i++)
961 ld->ld_defhost[i] = toupper(ld->ld_defhost[i]);
965 if (krb5_init_context(&context))
968 if (krb5_cc_default(context, &v5Cache))
970 if (krb5_cc_start_seq_get(context, v5Cache, &v5Cursor))
973 memset(&creds, '\0', sizeof(creds));
975 while (!(code = krb5_cc_next_cred(context, v5Cache, &v5Cursor, &creds)))
977 if (krb5_unparse_name(context, creds.server, &sServerName))
979 krb5_free_cred_contents(context, &creds);
982 if (!memcmp(sServerName, service_name, strlen(service_name)))
984 krb5_cc_remove_cred(context, v5Cache, 0, &creds);
989 if ((code == KRB5_CC_END) || (code == KRB5_CC_NOTFOUND))
991 krb5_cc_end_seq_get(context, v5Cache, &v5Cursor);
996 if ((v5Cache != NULL) && (context != NULL))
997 krb5_cc_close(context, v5Cache);
999 krb5_free_context(context);