This merges in my 'always use ADS' patch. Tested on a mix of NT and ADS
[Samba/gebeck_regimport.git] / source3 / libads / sasl.c
blob1ab71c6ee514fbae1ddd25699ba09fe8b026cd33
1 /*
2 Unix SMB/CIFS implementation.
3 ads sasl code
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.
21 #include "includes.h"
23 #ifdef HAVE_LDAP
25 /*
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};
32 DATA_BLOB msg1;
33 DATA_BLOB blob, chal1, chal2, auth;
34 uint8 challenge[8];
35 uint8 nthash[24], lmhash[24], sess_key[16];
36 uint32 neg_flags;
37 struct berval cred, *scred;
38 ADS_STATUS status;
39 int rc;
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",
54 "NTLMSSP",
55 NTLMSSP_NEGOTIATE,
56 neg_flags,
57 sess_key, 16);
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);
69 goto failed;
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);
78 goto failed;
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",
93 "NTLMSSP",
94 NTLMSSP_AUTH,
95 lmhash, 24,
96 nthash, 24,
97 lp_workgroup(),
98 ads->auth.user_name,
99 global_myname(),
100 sess_key, 16,
101 neg_flags);
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);
116 failed:
117 return status;
121 perform a LDAP/SASL/SPNEGO/KRB5 bind
123 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
125 DATA_BLOB blob;
126 struct berval cred, *scred;
127 DATA_BLOB session_key;
128 int rc;
130 rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key);
132 if (rc) {
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;
154 int rc, i;
155 ADS_STATUS status;
156 DATA_BLOB blob;
157 char *principal;
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);
165 goto failed;
168 blob = data_blob(scred->bv_val, scred->bv_len);
170 ber_bvfree(scred);
172 #if 0
173 file_save("sasl_spnego.dat", blob.data, blob.length);
174 #endif
176 /* the server sent us the first part of the SPNEGO exchange in the negprot
177 reply */
178 if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
179 data_blob_free(&blob);
180 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
181 goto failed;
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;
192 free(OIDs[i]);
194 DEBUG(3,("got principal=%s\n", principal));
196 #ifdef HAVE_KRB5
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))
201 return 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)) {
212 return status;
215 #endif
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);
222 failed:
223 return status;
226 #ifdef HAVE_GSSAPI
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 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 ret_flags, conf_state;
244 struct berval cred;
245 struct berval *scred;
246 int i=0;
247 int gss_rc, rc;
248 uint8 *p;
249 uint32 max_msg_size;
250 char *sname;
251 unsigned sec_layer;
252 ADS_STATUS status;
253 krb5_principal principal;
254 krb5_context ctx;
255 krb5_enctype enc_types[] = {
256 #ifdef ENCTYPE_ARCFOUR_HMAC
257 ENCTYPE_ARCFOUR_HMAC,
258 #endif
259 ENCTYPE_DES_CBC_MD5,
260 ENCTYPE_NULL};
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);
270 free(sname);
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);
277 if (gss_rc) {
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,
288 GSS_C_NO_CREDENTIAL,
289 &context_handle,
290 serv_name,
291 mech_type,
292 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
294 NULL,
295 &input_token,
296 NULL,
297 &output_token,
298 &ret_flags,
299 NULL);
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);
307 goto failed;
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,
314 &scred);
315 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
316 status = ADS_ERROR(rc);
317 goto failed;
320 if (output_token.value) {
321 gss_release_buffer(&minor_status, &output_token);
324 if (scred) {
325 input_token.value = scred->bv_val;
326 input_token.length = scred->bv_len;
327 } else {
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);
339 if (gss_rc) {
340 status = ADS_ERROR_GSS(gss_rc, minor_status);
341 goto failed;
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];
351 sec_layer = *p;
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;
362 *p++ = max_msg_size;
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,
370 &input_token);
371 if (gss_rc) {
372 status = ADS_ERROR_GSS(gss_rc, minor_status);
373 goto failed;
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,
382 &scred);
383 status = ADS_ERROR(rc);
385 gss_release_buffer(&minor_status, &input_token);
387 failed:
388 return status;
390 #endif
392 /* mapping between SASL mechanisms and functions */
393 static struct {
394 const char *name;
395 ADS_STATUS (*fn)(ADS_STRUCT *);
396 } sasl_mechanisms[] = {
397 {"GSS-SPNEGO", ads_sasl_spnego_bind},
398 #ifdef HAVE_GSSAPI
399 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
400 #endif
401 {NULL, NULL}
404 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
406 const char *attrs[] = {"supportedSASLMechanisms", NULL};
407 char **values;
408 ADS_STATUS status;
409 int i, j;
410 void *res;
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);
426 ldap_msgfree(res);
427 return status;
432 ldap_value_free(values);
433 ldap_msgfree(res);
434 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
437 #endif