2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 2001
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program 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
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
27 we fit on one socket??)
29 static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
31 const char *mechs[] = {OID_NTLMSSP, NULL};
33 DATA_BLOB blob, chal1, chal2, auth;
35 uint8 nthash[24], lmhash[24], sess_key[16];
37 struct berval cred, *scred;
41 if (!ads->auth.password) {
42 /* No password, don't segfault below... */
43 return ADS_ERROR_NT(NT_STATUS_LOGON_FAILURE);
46 neg_flags = NTLMSSP_NEGOTIATE_UNICODE |
47 NTLMSSP_NEGOTIATE_128 |
48 NTLMSSP_NEGOTIATE_NTLM;
50 memset(sess_key, 0, 16);
52 /* generate the ntlmssp negotiate packet */
53 msrpc_gen(&blob, "CddB",
59 /* and wrap it in a SPNEGO wrapper */
60 msg1 = gen_negTokenTarg(mechs, blob);
61 data_blob_free(&blob);
63 cred.bv_val = msg1.data;
64 cred.bv_len = msg1.length;
66 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
67 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
68 status = ADS_ERROR(rc);
72 blob = data_blob(scred->bv_val, scred->bv_len);
74 /* the server gives us back two challenges */
75 if (!spnego_parse_challenge(blob, &chal1, &chal2)) {
76 DEBUG(3,("Failed to parse challenges\n"));
77 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
81 data_blob_free(&blob);
83 /* encrypt the password with the challenge */
84 memcpy(challenge, chal1.data + 24, 8);
85 SMBencrypt(ads->auth.password, challenge,lmhash);
86 SMBNTencrypt(ads->auth.password, challenge,nthash);
88 data_blob_free(&chal1);
89 data_blob_free(&chal2);
91 /* this generates the actual auth packet */
92 msrpc_gen(&blob, "CdBBUUUBd",
103 /* wrap it in SPNEGO */
104 auth = spnego_gen_auth(blob);
106 data_blob_free(&blob);
108 /* now send the auth packet and we should be done */
109 cred.bv_val = auth.data;
110 cred.bv_len = auth.length;
112 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
114 return ADS_ERROR(rc);
121 perform a LDAP/SASL/SPNEGO/KRB5 bind
123 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
126 struct berval cred, *scred;
129 blob = spnego_gen_negTokenTarg(principal, ads->auth.time_offset);
132 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
135 /* now send the auth packet and we should be done */
136 cred.bv_val = blob.data;
137 cred.bv_len = blob.length;
139 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
141 data_blob_free(&blob);
143 return ADS_ERROR(rc);
147 this performs a SASL/SPNEGO bind
149 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
151 struct berval *scred=NULL;
156 char *OIDs[ASN1_MAX_OIDS];
157 BOOL got_kerberos_mechanism = False;
159 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
161 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
162 status = ADS_ERROR(rc);
166 blob = data_blob(scred->bv_val, scred->bv_len);
169 file_save("sasl_spnego.dat", blob.data, blob.length);
172 /* the server sent us the first part of the SPNEGO exchange in the negprot
174 if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
175 data_blob_free(&blob);
176 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
179 data_blob_free(&blob);
181 /* make sure the server understands kerberos */
182 for (i=0;OIDs[i];i++) {
183 DEBUG(3,("got OID=%s\n", OIDs[i]));
184 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
185 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
186 got_kerberos_mechanism = True;
190 DEBUG(3,("got principal=%s\n", principal));
193 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
194 got_kerberos_mechanism) {
195 status = ads_sasl_spnego_krb5_bind(ads, principal);
196 if (ADS_ERR_OK(status))
198 if (ads_kinit_password(ads) == 0) {
199 status = ads_sasl_spnego_krb5_bind(ads, principal);
201 if (ADS_ERR_OK(status))
206 /* lets do NTLMSSP ... this has the big advantage that we don't need
207 to sync clocks, and we don't rely on special versions of the krb5
208 library for HMAC_MD4 encryption */
209 return ads_sasl_spnego_ntlmssp_bind(ads);
216 #define MAX_GSS_PASSES 3
218 /* this performs a SASL/gssapi bind
219 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
220 is very dependent on correctly configured DNS whereas
221 this routine is much less fragile
222 see RFC2078 and RFC2222 for details
224 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
227 gss_name_t serv_name;
228 gss_buffer_desc input_name;
229 gss_ctx_id_t context_handle;
230 gss_OID mech_type = GSS_C_NULL_OID;
231 gss_buffer_desc output_token, input_token;
232 OM_uint32 ret_flags, conf_state;
234 struct berval *scred;
242 krb5_principal principal;
244 krb5_enctype enc_types[] = {
245 #ifdef ENCTYPE_ARCFOUR_HMAC
246 ENCTYPE_ARCFOUR_HMAC,
250 gss_OID_desc nt_principal =
251 {10, "\052\206\110\206\367\022\001\002\002\002"};
253 /* we need to fetch a service ticket as the ldap user in the
254 servers realm, regardless of our realm */
255 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
256 krb5_init_context(&ctx);
257 krb5_set_default_tgs_ktypes(ctx, enc_types);
258 krb5_parse_name(ctx, sname, &principal);
260 krb5_free_context(ctx);
262 input_name.value = &principal;
263 input_name.length = sizeof(principal);
265 gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
267 return ADS_ERROR_GSS(gss_rc, minor_status);
270 context_handle = GSS_C_NO_CONTEXT;
272 input_token.value = NULL;
273 input_token.length = 0;
275 for (i=0; i < MAX_GSS_PASSES; i++) {
276 gss_rc = gss_init_sec_context(&minor_status,
281 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
290 if (input_token.value) {
291 gss_release_buffer(&minor_status, &input_token);
294 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
295 status = ADS_ERROR_GSS(gss_rc, minor_status);
299 cred.bv_val = output_token.value;
300 cred.bv_len = output_token.length;
302 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
304 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
305 status = ADS_ERROR(rc);
309 if (output_token.value) {
310 gss_release_buffer(&minor_status, &output_token);
314 input_token.value = scred->bv_val;
315 input_token.length = scred->bv_len;
317 input_token.value = NULL;
318 input_token.length = 0;
321 if (gss_rc == 0) break;
324 gss_release_name(&minor_status, &serv_name);
326 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
329 status = ADS_ERROR_GSS(gss_rc, minor_status);
333 gss_release_buffer(&minor_status, &input_token);
335 p = (uint8 *)output_token.value;
337 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
339 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
342 gss_release_buffer(&minor_status, &output_token);
344 output_token.value = malloc(strlen(ads->config.bind_path) + 8);
345 p = output_token.value;
347 *p++ = 1; /* no sign & seal selection */
348 /* choose the same size as the server gave us */
349 *p++ = max_msg_size>>16;
350 *p++ = max_msg_size>>8;
352 snprintf(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
355 output_token.length = PTR_DIFF(p, output_token.value);
357 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
358 &output_token, &conf_state,
361 status = ADS_ERROR_GSS(gss_rc, minor_status);
365 free(output_token.value);
367 cred.bv_val = input_token.value;
368 cred.bv_len = input_token.length;
370 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
372 status = ADS_ERROR(rc);
374 gss_release_buffer(&minor_status, &input_token);
381 /* mapping between SASL mechanisms and functions */
384 ADS_STATUS (*fn)(ADS_STRUCT *);
385 } sasl_mechanisms[] = {
386 {"GSS-SPNEGO", ads_sasl_spnego_bind},
388 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
393 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
395 const char *attrs[] = {"supportedSASLMechanisms", NULL};
401 /* get a list of supported SASL mechanisms */
402 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
403 if (!ADS_ERR_OK(status)) return status;
405 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
407 /* try our supported mechanisms in order */
408 for (i=0;sasl_mechanisms[i].name;i++) {
409 /* see if the server supports it */
410 for (j=0;values && values[j];j++) {
411 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
412 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
413 status = sasl_mechanisms[i].fn(ads);
414 ldap_value_free(values);
421 ldap_value_free(values);
423 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);