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 return ads_sasl_spnego_krb5_bind(ads, principal);
204 /* lets do NTLMSSP ... this has the big advantage that we don't need
205 to sync clocks, and we don't rely on special versions of the krb5
206 library for HMAC_MD4 encryption */
207 return ads_sasl_spnego_ntlmssp_bind(ads);
214 #define MAX_GSS_PASSES 3
216 /* this performs a SASL/gssapi bind
217 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
218 is very dependent on correctly configured DNS whereas
219 this routine is much less fragile
220 see RFC2078 and RFC2222 for details
222 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
225 gss_name_t serv_name;
226 gss_buffer_desc input_name;
227 gss_ctx_id_t context_handle;
228 gss_OID mech_type = GSS_C_NULL_OID;
229 gss_buffer_desc output_token, input_token;
230 OM_uint32 ret_flags, conf_state;
232 struct berval *scred;
240 krb5_principal principal;
242 krb5_enctype enc_types[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL};
243 gss_OID_desc nt_principal =
244 {10, "\052\206\110\206\367\022\001\002\002\002"};
246 /* we need to fetch a service ticket as the ldap user in the
247 servers realm, regardless of our realm */
248 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
249 krb5_init_context(&ctx);
250 krb5_set_default_tgs_ktypes(ctx, enc_types);
251 krb5_parse_name(ctx, sname, &principal);
253 krb5_free_context(ctx);
255 input_name.value = &principal;
256 input_name.length = sizeof(principal);
258 gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
260 return ADS_ERROR_GSS(gss_rc, minor_status);
263 context_handle = GSS_C_NO_CONTEXT;
265 input_token.value = NULL;
266 input_token.length = 0;
268 for (i=0; i < MAX_GSS_PASSES; i++) {
269 gss_rc = gss_init_sec_context(&minor_status,
274 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
283 if (input_token.value) {
284 gss_release_buffer(&minor_status, &input_token);
287 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
288 status = ADS_ERROR_GSS(gss_rc, minor_status);
292 cred.bv_val = output_token.value;
293 cred.bv_len = output_token.length;
295 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
297 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
298 status = ADS_ERROR(rc);
302 if (output_token.value) {
303 gss_release_buffer(&minor_status, &output_token);
307 input_token.value = scred->bv_val;
308 input_token.length = scred->bv_len;
310 input_token.value = NULL;
311 input_token.length = 0;
314 if (gss_rc == 0) break;
317 gss_release_name(&minor_status, &serv_name);
319 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
322 status = ADS_ERROR_GSS(gss_rc, minor_status);
326 gss_release_buffer(&minor_status, &input_token);
328 p = (uint8 *)output_token.value;
330 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
332 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
335 gss_release_buffer(&minor_status, &output_token);
337 output_token.value = malloc(strlen(ads->config.bind_path) + 8);
338 p = output_token.value;
340 *p++ = 1; /* no sign & seal selection */
341 /* choose the same size as the server gave us */
342 *p++ = max_msg_size>>16;
343 *p++ = max_msg_size>>8;
345 snprintf(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
348 output_token.length = PTR_DIFF(p, output_token.value);
350 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
351 &output_token, &conf_state,
354 status = ADS_ERROR_GSS(gss_rc, minor_status);
358 free(output_token.value);
360 cred.bv_val = input_token.value;
361 cred.bv_len = input_token.length;
363 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
365 status = ADS_ERROR(rc);
367 gss_release_buffer(&minor_status, &input_token);
374 /* mapping between SASL mechanisms and functions */
377 ADS_STATUS (*fn)(ADS_STRUCT *);
378 } sasl_mechanisms[] = {
379 {"GSS-SPNEGO", ads_sasl_spnego_bind},
381 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
386 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
388 const char *attrs[] = {"supportedSASLMechanisms", NULL};
394 /* get a list of supported SASL mechanisms */
395 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
396 if (!ADS_ERR_OK(status)) return status;
398 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
400 /* try our supported mechanisms in order */
401 for (i=0;sasl_mechanisms[i].name;i++) {
402 /* see if the server supports it */
403 for (j=0;values && values[j];j++) {
404 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
405 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
406 status = sasl_mechanisms[i].fn(ads);
407 ldap_value_free(values);
414 ldap_value_free(values);
416 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);