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;
39 extern pstring global_myname;
42 if (!ads->auth.password) {
43 /* No password, don't segfault below... */
44 return ADS_ERROR_NT(NT_STATUS_LOGON_FAILURE);
47 neg_flags = NTLMSSP_NEGOTIATE_UNICODE |
48 NTLMSSP_NEGOTIATE_128 |
49 NTLMSSP_NEGOTIATE_NTLM;
51 memset(sess_key, 0, 16);
53 /* generate the ntlmssp negotiate packet */
54 msrpc_gen(&blob, "CddB",
60 /* and wrap it in a SPNEGO wrapper */
61 msg1 = gen_negTokenTarg(mechs, blob);
62 data_blob_free(&blob);
64 cred.bv_val = msg1.data;
65 cred.bv_len = msg1.length;
67 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
68 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
69 status = ADS_ERROR(rc);
73 blob = data_blob(scred->bv_val, scred->bv_len);
75 /* the server gives us back two challenges */
76 if (!spnego_parse_challenge(blob, &chal1, &chal2)) {
77 DEBUG(3,("Failed to parse challenges\n"));
78 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
82 data_blob_free(&blob);
84 /* encrypt the password with the challenge */
85 memcpy(challenge, chal1.data + 24, 8);
86 SMBencrypt(ads->auth.password, challenge,lmhash);
87 SMBNTencrypt(ads->auth.password, challenge,nthash);
89 data_blob_free(&chal1);
90 data_blob_free(&chal2);
92 /* this generates the actual auth packet */
93 msrpc_gen(&blob, "CdBBUUUBd",
104 /* wrap it in SPNEGO */
105 auth = spnego_gen_auth(blob);
107 data_blob_free(&blob);
109 /* now send the auth packet and we should be done */
110 cred.bv_val = auth.data;
111 cred.bv_len = auth.length;
113 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
115 return ADS_ERROR(rc);
122 perform a LDAP/SASL/SPNEGO/KRB5 bind
124 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
127 struct berval cred, *scred;
130 blob = spnego_gen_negTokenTarg(principal, ads->auth.time_offset);
133 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
136 /* now send the auth packet and we should be done */
137 cred.bv_val = 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);
144 return ADS_ERROR(rc);
148 this performs a SASL/SPNEGO bind
150 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
152 struct berval *scred=NULL;
157 char *OIDs[ASN1_MAX_OIDS];
158 BOOL got_kerberos_mechanism = False;
160 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
162 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
163 status = ADS_ERROR(rc);
167 blob = data_blob(scred->bv_val, scred->bv_len);
170 file_save("sasl_spnego.dat", blob.data, blob.length);
173 /* the server sent us the first part of the SPNEGO exchange in the negprot
175 if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
176 data_blob_free(&blob);
177 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
180 data_blob_free(&blob);
182 /* make sure the server understands kerberos */
183 for (i=0;OIDs[i];i++) {
184 DEBUG(3,("got OID=%s\n", OIDs[i]));
185 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
186 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
187 got_kerberos_mechanism = True;
191 DEBUG(3,("got principal=%s\n", principal));
194 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
195 got_kerberos_mechanism) {
196 status = ads_sasl_spnego_krb5_bind(ads, principal);
197 if (ADS_ERR_OK(status))
199 if (ads_kinit_password(ads) == 0) {
200 status = ads_sasl_spnego_krb5_bind(ads, principal);
202 if (ADS_ERR_OK(status))
207 /* lets do NTLMSSP ... this has the big advantage that we don't need
208 to sync clocks, and we don't rely on special versions of the krb5
209 library for HMAC_MD4 encryption */
210 return ads_sasl_spnego_ntlmssp_bind(ads);
217 #define MAX_GSS_PASSES 3
219 /* this performs a SASL/gssapi bind
220 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
221 is very dependent on correctly configured DNS whereas
222 this routine is much less fragile
223 see RFC2078 and RFC2222 for details
225 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
228 gss_name_t serv_name;
229 gss_buffer_desc input_name;
230 gss_ctx_id_t context_handle;
231 gss_OID mech_type = GSS_C_NULL_OID;
232 gss_buffer_desc output_token, input_token;
233 OM_uint32 ret_flags, conf_state;
235 struct berval *scred;
243 krb5_principal principal;
245 krb5_enctype enc_types[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL};
246 gss_OID_desc nt_principal =
247 {10, "\052\206\110\206\367\022\001\002\002\002"};
249 /* we need to fetch a service ticket as the ldap user in the
250 servers realm, regardless of our realm */
251 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
252 krb5_init_context(&ctx);
253 krb5_set_default_tgs_ktypes(ctx, enc_types);
254 krb5_parse_name(ctx, sname, &principal);
256 krb5_free_context(ctx);
258 input_name.value = &principal;
259 input_name.length = sizeof(principal);
261 gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
263 return ADS_ERROR_GSS(gss_rc, minor_status);
266 context_handle = GSS_C_NO_CONTEXT;
268 input_token.value = NULL;
269 input_token.length = 0;
271 for (i=0; i < MAX_GSS_PASSES; i++) {
272 gss_rc = gss_init_sec_context(&minor_status,
277 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
286 if (input_token.value) {
287 gss_release_buffer(&minor_status, &input_token);
290 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
291 status = ADS_ERROR_GSS(gss_rc, minor_status);
295 cred.bv_val = output_token.value;
296 cred.bv_len = output_token.length;
298 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
300 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
301 status = ADS_ERROR(rc);
305 if (output_token.value) {
306 gss_release_buffer(&minor_status, &output_token);
310 input_token.value = scred->bv_val;
311 input_token.length = scred->bv_len;
313 input_token.value = NULL;
314 input_token.length = 0;
317 if (gss_rc == 0) break;
320 gss_release_name(&minor_status, &serv_name);
322 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
325 status = ADS_ERROR_GSS(gss_rc, minor_status);
329 gss_release_buffer(&minor_status, &input_token);
331 p = (uint8 *)output_token.value;
333 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
335 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
338 gss_release_buffer(&minor_status, &output_token);
340 output_token.value = malloc(strlen(ads->config.bind_path) + 8);
341 p = output_token.value;
343 *p++ = 1; /* no sign & seal selection */
344 /* choose the same size as the server gave us */
345 *p++ = max_msg_size>>16;
346 *p++ = max_msg_size>>8;
348 snprintf(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
351 output_token.length = PTR_DIFF(p, output_token.value);
353 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
354 &output_token, &conf_state,
357 status = ADS_ERROR_GSS(gss_rc, minor_status);
361 free(output_token.value);
363 cred.bv_val = input_token.value;
364 cred.bv_len = input_token.length;
366 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
368 status = ADS_ERROR(rc);
370 gss_release_buffer(&minor_status, &input_token);
377 /* mapping between SASL mechanisms and functions */
380 ADS_STATUS (*fn)(ADS_STRUCT *);
381 } sasl_mechanisms[] = {
382 {"GSS-SPNEGO", ads_sasl_spnego_bind},
384 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
389 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
391 const char *attrs[] = {"supportedSASLMechanisms", NULL};
397 /* get a list of supported SASL mechanisms */
398 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
399 if (!ADS_ERR_OK(status)) return status;
401 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
403 /* try our supported mechanisms in order */
404 for (i=0;sasl_mechanisms[i].name;i++) {
405 /* see if the server supports it */
406 for (j=0;values && values[j];j++) {
407 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
408 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
409 status = sasl_mechanisms[i].fn(ads);
410 ldap_value_free(values);
417 ldap_value_free(values);
419 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);