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 /* only fallback to NTLMSSP if allowed */
202 if (ADS_ERR_OK(status) ||
203 !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
209 /* lets do NTLMSSP ... this has the big advantage that we don't need
210 to sync clocks, and we don't rely on special versions of the krb5
211 library for HMAC_MD4 encryption */
212 return ads_sasl_spnego_ntlmssp_bind(ads);
219 #define MAX_GSS_PASSES 3
221 /* this performs a SASL/gssapi bind
222 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
223 is very dependent on correctly configured DNS whereas
224 this routine is much less fragile
225 see RFC2078 and RFC2222 for details
227 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
230 gss_name_t serv_name;
231 gss_buffer_desc input_name;
232 gss_ctx_id_t context_handle;
233 gss_OID mech_type = GSS_C_NULL_OID;
234 gss_buffer_desc output_token, input_token;
235 OM_uint32 ret_flags, conf_state;
237 struct berval *scred;
245 krb5_principal principal;
247 krb5_enctype enc_types[] = {
248 #ifdef ENCTYPE_ARCFOUR_HMAC
249 ENCTYPE_ARCFOUR_HMAC,
253 gss_OID_desc nt_principal =
254 {10, "\052\206\110\206\367\022\001\002\002\002"};
256 /* we need to fetch a service ticket as the ldap user in the
257 servers realm, regardless of our realm */
258 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
259 krb5_init_context(&ctx);
260 krb5_set_default_tgs_ktypes(ctx, enc_types);
261 krb5_parse_name(ctx, sname, &principal);
263 krb5_free_context(ctx);
265 input_name.value = &principal;
266 input_name.length = sizeof(principal);
268 gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
270 return ADS_ERROR_GSS(gss_rc, minor_status);
273 context_handle = GSS_C_NO_CONTEXT;
275 input_token.value = NULL;
276 input_token.length = 0;
278 for (i=0; i < MAX_GSS_PASSES; i++) {
279 gss_rc = gss_init_sec_context(&minor_status,
284 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
293 if (input_token.value) {
294 gss_release_buffer(&minor_status, &input_token);
297 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
298 status = ADS_ERROR_GSS(gss_rc, minor_status);
302 cred.bv_val = output_token.value;
303 cred.bv_len = output_token.length;
305 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
307 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
308 status = ADS_ERROR(rc);
312 if (output_token.value) {
313 gss_release_buffer(&minor_status, &output_token);
317 input_token.value = scred->bv_val;
318 input_token.length = scred->bv_len;
320 input_token.value = NULL;
321 input_token.length = 0;
324 if (gss_rc == 0) break;
327 gss_release_name(&minor_status, &serv_name);
329 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
332 status = ADS_ERROR_GSS(gss_rc, minor_status);
336 gss_release_buffer(&minor_status, &input_token);
338 p = (uint8 *)output_token.value;
340 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
342 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
345 gss_release_buffer(&minor_status, &output_token);
347 output_token.value = malloc(strlen(ads->config.bind_path) + 8);
348 p = output_token.value;
350 *p++ = 1; /* no sign & seal selection */
351 /* choose the same size as the server gave us */
352 *p++ = max_msg_size>>16;
353 *p++ = max_msg_size>>8;
355 snprintf(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
358 output_token.length = PTR_DIFF(p, output_token.value);
360 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
361 &output_token, &conf_state,
364 status = ADS_ERROR_GSS(gss_rc, minor_status);
368 free(output_token.value);
370 cred.bv_val = input_token.value;
371 cred.bv_len = input_token.length;
373 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
375 status = ADS_ERROR(rc);
377 gss_release_buffer(&minor_status, &input_token);
384 /* mapping between SASL mechanisms and functions */
387 ADS_STATUS (*fn)(ADS_STRUCT *);
388 } sasl_mechanisms[] = {
389 {"GSS-SPNEGO", ads_sasl_spnego_bind},
391 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
396 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
398 const char *attrs[] = {"supportedSASLMechanisms", NULL};
404 /* get a list of supported SASL mechanisms */
405 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
406 if (!ADS_ERR_OK(status)) return status;
408 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
410 /* try our supported mechanisms in order */
411 for (i=0;sasl_mechanisms[i].name;i++) {
412 /* see if the server supports it */
413 for (j=0;values && values[j];j++) {
414 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
415 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
416 status = sasl_mechanisms[i].fn(ads);
417 ldap_value_free(values);
424 ldap_value_free(values);
426 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);