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 && ads_kinit_password(ads) == 0) {
196 return ads_sasl_spnego_krb5_bind(ads, principal);
200 /* lets do NTLMSSP ... this has the big advantage that we don't need
201 to sync clocks, and we don't rely on special versions of the krb5
202 library for HMAC_MD4 encryption */
203 return ads_sasl_spnego_ntlmssp_bind(ads);
210 #define MAX_GSS_PASSES 3
212 /* this performs a SASL/gssapi bind
213 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
214 is very dependent on correctly configured DNS whereas
215 this routine is much less fragile
216 see RFC2078 and RFC2222 for details
218 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
221 gss_name_t serv_name;
222 gss_buffer_desc input_name;
223 gss_ctx_id_t context_handle;
224 gss_OID mech_type = GSS_C_NULL_OID;
225 gss_buffer_desc output_token, input_token;
226 OM_uint32 ret_flags, conf_state;
228 struct berval *scred;
236 krb5_principal principal;
238 krb5_enctype enc_types[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL};
239 gss_OID_desc nt_principal =
240 {10, "\052\206\110\206\367\022\001\002\002\002"};
242 /* we need to fetch a service ticket as the ldap user in the
243 servers realm, regardless of our realm */
244 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
245 krb5_init_context(&ctx);
246 krb5_set_default_tgs_ktypes(ctx, enc_types);
247 krb5_parse_name(ctx, sname, &principal);
249 krb5_free_context(ctx);
251 input_name.value = &principal;
252 input_name.length = sizeof(principal);
254 gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
256 return ADS_ERROR_GSS(gss_rc, minor_status);
259 context_handle = GSS_C_NO_CONTEXT;
261 input_token.value = NULL;
262 input_token.length = 0;
264 for (i=0; i < MAX_GSS_PASSES; i++) {
265 gss_rc = gss_init_sec_context(&minor_status,
270 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
279 if (input_token.value) {
280 gss_release_buffer(&minor_status, &input_token);
283 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
284 status = ADS_ERROR_GSS(gss_rc, minor_status);
288 cred.bv_val = output_token.value;
289 cred.bv_len = output_token.length;
291 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
293 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
294 status = ADS_ERROR(rc);
298 if (output_token.value) {
299 gss_release_buffer(&minor_status, &output_token);
303 input_token.value = scred->bv_val;
304 input_token.length = scred->bv_len;
306 input_token.value = NULL;
307 input_token.length = 0;
310 if (gss_rc == 0) break;
313 gss_release_name(&minor_status, &serv_name);
315 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
318 status = ADS_ERROR_GSS(gss_rc, minor_status);
322 gss_release_buffer(&minor_status, &input_token);
324 p = (uint8 *)output_token.value;
326 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
328 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
331 gss_release_buffer(&minor_status, &output_token);
333 output_token.value = malloc(strlen(ads->config.bind_path) + 8);
334 p = output_token.value;
336 *p++ = 1; /* no sign & seal selection */
337 /* choose the same size as the server gave us */
338 *p++ = max_msg_size>>16;
339 *p++ = max_msg_size>>8;
341 snprintf(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
344 output_token.length = PTR_DIFF(p, output_token.value);
346 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
347 &output_token, &conf_state,
350 status = ADS_ERROR_GSS(gss_rc, minor_status);
354 free(output_token.value);
356 cred.bv_val = input_token.value;
357 cred.bv_len = input_token.length;
359 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
361 status = ADS_ERROR(rc);
363 gss_release_buffer(&minor_status, &input_token);
370 /* mapping between SASL mechanisms and functions */
373 ADS_STATUS (*fn)(ADS_STRUCT *);
374 } sasl_mechanisms[] = {
375 {"GSS-SPNEGO", ads_sasl_spnego_bind},
377 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
382 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
384 const char *attrs[] = {"supportedSASLMechanisms", NULL};
390 /* get a list of supported SASL mechanisms */
391 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
392 if (!ADS_ERR_OK(status)) return status;
394 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
396 /* try our supported mechanisms in order */
397 for (i=0;sasl_mechanisms[i].name;i++) {
398 /* see if the server supports it */
399 for (j=0;values && values[j];j++) {
400 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
401 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
402 status = sasl_mechanisms[i].fn(ads);
403 ldap_value_free(values);
410 ldap_value_free(values);
412 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);