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 = (char *)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 = (char *)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;
127 DATA_BLOB session_key;
130 rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key);
133 return ADS_ERROR_KRB5(rc);
136 /* now send the auth packet and we should be done */
137 cred.bv_val = (char *)blob.data;
138 cred.bv_len = blob.length;
140 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
142 data_blob_free(&blob);
143 data_blob_free(&session_key);
145 return ADS_ERROR(rc);
149 this performs a SASL/SPNEGO bind
151 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
153 struct berval *scred=NULL;
158 char *OIDs[ASN1_MAX_OIDS];
159 BOOL got_kerberos_mechanism = False;
161 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
163 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
164 status = ADS_ERROR(rc);
168 blob = data_blob(scred->bv_val, scred->bv_len);
173 file_save("sasl_spnego.dat", blob.data, blob.length);
176 /* the server sent us the first part of the SPNEGO exchange in the negprot
178 if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
179 data_blob_free(&blob);
180 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
183 data_blob_free(&blob);
185 /* make sure the server understands kerberos */
186 for (i=0;OIDs[i];i++) {
187 DEBUG(3,("got OID=%s\n", OIDs[i]));
188 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
189 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
190 got_kerberos_mechanism = True;
194 DEBUG(3,("got principal=%s\n", principal));
197 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
198 got_kerberos_mechanism) {
199 status = ads_sasl_spnego_krb5_bind(ads, principal);
200 if (ADS_ERR_OK(status))
203 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
205 if (ADS_ERR_OK(status)) {
206 status = ads_sasl_spnego_krb5_bind(ads, principal);
209 /* only fallback to NTLMSSP if allowed */
210 if (ADS_ERR_OK(status) ||
211 !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
217 /* lets do NTLMSSP ... this has the big advantage that we don't need
218 to sync clocks, and we don't rely on special versions of the krb5
219 library for HMAC_MD4 encryption */
220 return ads_sasl_spnego_ntlmssp_bind(ads);
227 #define MAX_GSS_PASSES 3
229 /* this performs a SASL/gssapi bind
230 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
231 is very dependent on correctly configured DNS whereas
232 this routine is much less fragile
233 see RFC2078 and RFC2222 for details
235 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
237 uint32_t minor_status;
238 gss_name_t serv_name;
239 gss_buffer_desc input_name;
240 gss_ctx_id_t context_handle;
241 gss_OID mech_type = GSS_C_NULL_OID;
242 gss_buffer_desc output_token, input_token;
243 uint32_t ret_flags, conf_state;
245 struct berval *scred;
249 uint32_t max_msg_size;
253 krb5_principal principal;
255 krb5_enctype enc_types[] = {
256 #ifdef ENCTYPE_ARCFOUR_HMAC
257 ENCTYPE_ARCFOUR_HMAC,
261 gss_OID_desc nt_principal =
262 {10, "\052\206\110\206\367\022\001\002\002\002"};
264 /* we need to fetch a service ticket as the ldap user in the
265 servers realm, regardless of our realm */
266 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
267 krb5_init_context(&ctx);
268 krb5_set_default_tgs_ktypes(ctx, enc_types);
269 krb5_parse_name(ctx, sname, &principal);
271 krb5_free_context(ctx);
273 input_name.value = &principal;
274 input_name.length = sizeof(principal);
276 gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
278 return ADS_ERROR_GSS(gss_rc, minor_status);
281 context_handle = GSS_C_NO_CONTEXT;
283 input_token.value = NULL;
284 input_token.length = 0;
286 for (i=0; i < MAX_GSS_PASSES; i++) {
287 gss_rc = gss_init_sec_context(&minor_status,
292 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
301 if (input_token.value) {
302 gss_release_buffer(&minor_status, &input_token);
305 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
306 status = ADS_ERROR_GSS(gss_rc, minor_status);
310 cred.bv_val = output_token.value;
311 cred.bv_len = output_token.length;
313 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
315 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
316 status = ADS_ERROR(rc);
320 if (output_token.value) {
321 gss_release_buffer(&minor_status, &output_token);
325 input_token.value = scred->bv_val;
326 input_token.length = scred->bv_len;
328 input_token.value = NULL;
329 input_token.length = 0;
332 if (gss_rc == 0) break;
335 gss_release_name(&minor_status, &serv_name);
337 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
338 (int *)&conf_state,NULL);
340 status = ADS_ERROR_GSS(gss_rc, minor_status);
344 gss_release_buffer(&minor_status, &input_token);
346 p = (uint8 *)output_token.value;
348 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
350 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
353 gss_release_buffer(&minor_status, &output_token);
355 output_token.value = malloc(strlen(ads->config.bind_path) + 8);
356 p = output_token.value;
358 *p++ = 1; /* no sign & seal selection */
359 /* choose the same size as the server gave us */
360 *p++ = max_msg_size>>16;
361 *p++ = max_msg_size>>8;
363 snprintf((char *)p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
364 p += strlen((const char *)p);
366 output_token.length = PTR_DIFF(p, output_token.value);
368 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
369 &output_token, (int *)&conf_state,
372 status = ADS_ERROR_GSS(gss_rc, minor_status);
376 free(output_token.value);
378 cred.bv_val = input_token.value;
379 cred.bv_len = input_token.length;
381 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
383 status = ADS_ERROR(rc);
385 gss_release_buffer(&minor_status, &input_token);
392 /* mapping between SASL mechanisms and functions */
395 ADS_STATUS (*fn)(ADS_STRUCT *);
396 } sasl_mechanisms[] = {
397 {"GSS-SPNEGO", ads_sasl_spnego_bind},
399 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
404 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
406 const char *attrs[] = {"supportedSASLMechanisms", NULL};
412 /* get a list of supported SASL mechanisms */
413 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
414 if (!ADS_ERR_OK(status)) return status;
416 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
418 /* try our supported mechanisms in order */
419 for (i=0;sasl_mechanisms[i].name;i++) {
420 /* see if the server supports it */
421 for (j=0;values && values[j];j++) {
422 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
423 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
424 status = sasl_mechanisms[i].fn(ads);
425 ldap_value_free(values);
432 ldap_value_free(values);
434 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);