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 neg_flags = NTLMSSP_NEGOTIATE_UNICODE |
43 NTLMSSP_NEGOTIATE_128 |
44 NTLMSSP_NEGOTIATE_NTLM;
46 memset(sess_key, 0, 16);
48 /* generate the ntlmssp negotiate packet */
49 msrpc_gen(&blob, "CddB",
55 /* and wrap it in a SPNEGO wrapper */
56 msg1 = gen_negTokenTarg(mechs, blob);
57 data_blob_free(&blob);
59 cred.bv_val = msg1.data;
60 cred.bv_len = msg1.length;
62 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
63 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
64 status = ADS_ERROR(rc);
68 blob = data_blob(scred->bv_val, scred->bv_len);
70 /* the server gives us back two challenges */
71 if (!spnego_parse_challenge(blob, &chal1, &chal2)) {
72 DEBUG(3,("Failed to parse challenges\n"));
73 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
77 data_blob_free(&blob);
79 /* encrypt the password with the challenge */
80 memcpy(challenge, chal1.data + 24, 8);
81 SMBencrypt(ads->auth.password, challenge,lmhash);
82 SMBNTencrypt(ads->auth.password, challenge,nthash);
84 data_blob_free(&chal1);
85 data_blob_free(&chal2);
87 /* this generates the actual auth packet */
88 msrpc_gen(&blob, "CdBBUUUBd",
99 /* wrap it in SPNEGO */
100 auth = spnego_gen_auth(blob);
102 data_blob_free(&blob);
104 /* now send the auth packet and we should be done */
105 cred.bv_val = auth.data;
106 cred.bv_len = auth.length;
108 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
110 return ADS_ERROR(rc);
117 perform a LDAP/SASL/SPNEGO/KRB5 bind
119 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
122 struct berval cred, *scred;
125 blob = spnego_gen_negTokenTarg(principal);
128 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
131 /* now send the auth packet and we should be done */
132 cred.bv_val = blob.data;
133 cred.bv_len = blob.length;
135 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
137 data_blob_free(&blob);
139 return ADS_ERROR(rc);
143 this performs a SASL/SPNEGO bind
145 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
147 struct berval *scred;
152 char *OIDs[ASN1_MAX_OIDS];
153 BOOL got_kerberos_mechanism = False;
155 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
157 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
158 status = ADS_ERROR(rc);
162 blob = data_blob(scred->bv_val, scred->bv_len);
165 file_save("sasl_spnego.dat", blob.data, blob.length);
168 /* the server sent us the first part of the SPNEGO exchange in the negprot
170 if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
171 data_blob_free(&blob);
172 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
175 data_blob_free(&blob);
177 /* make sure the server understands kerberos */
178 for (i=0;OIDs[i];i++) {
179 DEBUG(3,("got OID=%s\n", OIDs[i]));
180 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
181 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
182 got_kerberos_mechanism = True;
186 DEBUG(3,("got principal=%s\n", principal));
188 if (got_kerberos_mechanism && ads_kinit_password(ads) == 0) {
189 return ads_sasl_spnego_krb5_bind(ads, principal);
192 /* lets do NTLMSSP ... this has the big advantage that we don't need
193 to sync clocks, and we don't rely on special versions of the krb5
194 library for HMAC_MD4 encryption */
195 return ads_sasl_spnego_ntlmssp_bind(ads);
202 #define MAX_GSS_PASSES 3
204 /* this performs a SASL/gssapi bind
205 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
206 is very dependent on correctly configured DNS whereas
207 this routine is much less fragile
208 see RFC2078 and RFC2222 for details
210 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
213 gss_name_t serv_name;
214 gss_buffer_desc input_name;
215 gss_ctx_id_t context_handle;
216 gss_OID mech_type = GSS_C_NULL_OID;
217 gss_buffer_desc output_token, input_token;
218 OM_uint32 ret_flags, conf_state;
220 struct berval *scred;
228 krb5_principal principal;
230 krb5_enctype enc_types[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL};
231 gss_OID_desc nt_principal =
232 {10, "\052\206\110\206\367\022\001\002\002\002"};
234 /* we need to fetch a service ticket as the ldap user in the
235 servers realm, regardless of our realm */
236 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
237 krb5_init_context(&ctx);
238 krb5_set_default_tgs_ktypes(ctx, enc_types);
239 krb5_parse_name(ctx, sname, &principal);
241 krb5_free_context(ctx);
243 input_name.value = &principal;
244 input_name.length = sizeof(principal);
246 gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
248 return ADS_ERROR_GSS(gss_rc, minor_status);
251 context_handle = GSS_C_NO_CONTEXT;
253 input_token.value = NULL;
254 input_token.length = 0;
256 for (i=0; i < MAX_GSS_PASSES; i++) {
257 gss_rc = gss_init_sec_context(&minor_status,
262 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
271 if (input_token.value) {
272 gss_release_buffer(&minor_status, &input_token);
275 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
276 status = ADS_ERROR_GSS(gss_rc, minor_status);
280 cred.bv_val = output_token.value;
281 cred.bv_len = output_token.length;
283 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
285 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
286 status = ADS_ERROR(rc);
290 if (output_token.value) {
291 gss_release_buffer(&minor_status, &output_token);
295 input_token.value = scred->bv_val;
296 input_token.length = scred->bv_len;
298 input_token.value = NULL;
299 input_token.length = 0;
302 if (gss_rc == 0) break;
305 gss_release_name(&minor_status, &serv_name);
307 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
310 status = ADS_ERROR_GSS(gss_rc, minor_status);
314 gss_release_buffer(&minor_status, &input_token);
316 p = (uint8 *)output_token.value;
318 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
320 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
323 gss_release_buffer(&minor_status, &output_token);
325 output_token.value = malloc(strlen(ads->config.bind_path) + 8);
326 p = output_token.value;
328 *p++ = 1; /* no sign & seal selection */
329 /* choose the same size as the server gave us */
330 *p++ = max_msg_size>>16;
331 *p++ = max_msg_size>>8;
333 snprintf(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
336 output_token.length = PTR_DIFF(p, output_token.value);
338 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
339 &output_token, &conf_state,
342 status = ADS_ERROR_GSS(gss_rc, minor_status);
346 free(output_token.value);
348 cred.bv_val = input_token.value;
349 cred.bv_len = input_token.length;
351 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
353 status = ADS_ERROR(rc);
355 gss_release_buffer(&minor_status, &input_token);
362 /* mapping between SASL mechanisms and functions */
365 ADS_STATUS (*fn)(ADS_STRUCT *);
366 } sasl_mechanisms[] = {
367 {"GSS-SPNEGO", ads_sasl_spnego_bind},
369 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
374 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
376 const char *attrs[] = {"supportedSASLMechanisms", NULL};
382 /* get a list of supported SASL mechanisms */
383 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
384 if (!ADS_ERR_OK(status)) return status;
386 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
388 /* try our supported mechanisms in order */
389 for (i=0;sasl_mechanisms[i].name;i++) {
390 /* see if the server supports it */
391 for (j=0;values && values[j];j++) {
392 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
393 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
394 status = sasl_mechanisms[i].fn(ads);
395 ldap_value_free(values);
402 ldap_value_free(values);
404 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);