sync'ing up for 3.0alpha20 release
[Samba.git] / source / libads / sasl.c
blobf7dd01084a2f9c1fc70ec685522200ac39dd97dd
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_ADS
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 extern pstring global_myname;
40 int rc;
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",
55 "NTLMSSP",
56 NTLMSSP_NEGOTIATE,
57 neg_flags,
58 sess_key, 16);
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);
70 goto failed;
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);
79 goto failed;
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",
94 "NTLMSSP",
95 NTLMSSP_AUTH,
96 lmhash, 24,
97 nthash, 24,
98 lp_workgroup(),
99 ads->auth.user_name,
100 global_myname,
101 sess_key, 16,
102 neg_flags);
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);
117 failed:
118 return status;
122 perform a LDAP/SASL/SPNEGO/KRB5 bind
124 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
126 DATA_BLOB blob;
127 struct berval cred, *scred;
128 int rc;
130 blob = spnego_gen_negTokenTarg(principal, ads->auth.time_offset);
132 if (!blob.data) {
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;
153 int rc, i;
154 ADS_STATUS status;
155 DATA_BLOB blob;
156 char *principal;
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);
164 goto failed;
167 blob = data_blob(scred->bv_val, scred->bv_len);
169 #if 0
170 file_save("sasl_spnego.dat", blob.data, blob.length);
171 #endif
173 /* the server sent us the first part of the SPNEGO exchange in the negprot
174 reply */
175 if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
176 data_blob_free(&blob);
177 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
178 goto failed;
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;
189 free(OIDs[i]);
191 DEBUG(3,("got principal=%s\n", principal));
193 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
194 got_kerberos_mechanism && ads_kinit_password(ads) == 0) {
195 return ads_sasl_spnego_krb5_bind(ads, principal);
198 /* lets do NTLMSSP ... this has the big advantage that we don't need
199 to sync clocks, and we don't rely on special versions of the krb5
200 library for HMAC_MD4 encryption */
201 return ads_sasl_spnego_ntlmssp_bind(ads);
203 failed:
204 return status;
207 #ifdef HAVE_GSSAPI
208 #define MAX_GSS_PASSES 3
210 /* this performs a SASL/gssapi bind
211 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
212 is very dependent on correctly configured DNS whereas
213 this routine is much less fragile
214 see RFC2078 and RFC2222 for details
216 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
218 int minor_status;
219 gss_name_t serv_name;
220 gss_buffer_desc input_name;
221 gss_ctx_id_t context_handle;
222 gss_OID mech_type = GSS_C_NULL_OID;
223 gss_buffer_desc output_token, input_token;
224 OM_uint32 ret_flags, conf_state;
225 struct berval cred;
226 struct berval *scred;
227 int i=0;
228 int gss_rc, rc;
229 uint8 *p;
230 uint32 max_msg_size;
231 char *sname;
232 unsigned sec_layer;
233 ADS_STATUS status;
234 krb5_principal principal;
235 krb5_context ctx;
236 krb5_enctype enc_types[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL};
237 gss_OID_desc nt_principal =
238 {10, "\052\206\110\206\367\022\001\002\002\002"};
240 /* we need to fetch a service ticket as the ldap user in the
241 servers realm, regardless of our realm */
242 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
243 krb5_init_context(&ctx);
244 krb5_set_default_tgs_ktypes(ctx, enc_types);
245 krb5_parse_name(ctx, sname, &principal);
246 free(sname);
247 krb5_free_context(ctx);
249 input_name.value = &principal;
250 input_name.length = sizeof(principal);
252 gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
253 if (gss_rc) {
254 return ADS_ERROR_GSS(gss_rc, minor_status);
257 context_handle = GSS_C_NO_CONTEXT;
259 input_token.value = NULL;
260 input_token.length = 0;
262 for (i=0; i < MAX_GSS_PASSES; i++) {
263 gss_rc = gss_init_sec_context(&minor_status,
264 GSS_C_NO_CREDENTIAL,
265 &context_handle,
266 serv_name,
267 mech_type,
268 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
270 NULL,
271 &input_token,
272 NULL,
273 &output_token,
274 &ret_flags,
275 NULL);
277 if (input_token.value) {
278 gss_release_buffer(&minor_status, &input_token);
281 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
282 status = ADS_ERROR_GSS(gss_rc, minor_status);
283 goto failed;
286 cred.bv_val = output_token.value;
287 cred.bv_len = output_token.length;
289 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
290 &scred);
291 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
292 status = ADS_ERROR(rc);
293 goto failed;
296 if (output_token.value) {
297 gss_release_buffer(&minor_status, &output_token);
300 if (scred) {
301 input_token.value = scred->bv_val;
302 input_token.length = scred->bv_len;
303 } else {
304 input_token.value = NULL;
305 input_token.length = 0;
308 if (gss_rc == 0) break;
311 gss_release_name(&minor_status, &serv_name);
313 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
314 &conf_state,NULL);
315 if (gss_rc) {
316 status = ADS_ERROR_GSS(gss_rc, minor_status);
317 goto failed;
320 gss_release_buffer(&minor_status, &input_token);
322 p = (uint8 *)output_token.value;
324 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
326 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
327 sec_layer = *p;
329 gss_release_buffer(&minor_status, &output_token);
331 output_token.value = malloc(strlen(ads->config.bind_path) + 8);
332 p = output_token.value;
334 *p++ = 1; /* no sign & seal selection */
335 /* choose the same size as the server gave us */
336 *p++ = max_msg_size>>16;
337 *p++ = max_msg_size>>8;
338 *p++ = max_msg_size;
339 snprintf(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
340 p += strlen(p);
342 output_token.length = PTR_DIFF(p, output_token.value);
344 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
345 &output_token, &conf_state,
346 &input_token);
347 if (gss_rc) {
348 status = ADS_ERROR_GSS(gss_rc, minor_status);
349 goto failed;
352 free(output_token.value);
354 cred.bv_val = input_token.value;
355 cred.bv_len = input_token.length;
357 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
358 &scred);
359 status = ADS_ERROR(rc);
361 gss_release_buffer(&minor_status, &input_token);
363 failed:
364 return status;
366 #endif
368 /* mapping between SASL mechanisms and functions */
369 static struct {
370 const char *name;
371 ADS_STATUS (*fn)(ADS_STRUCT *);
372 } sasl_mechanisms[] = {
373 {"GSS-SPNEGO", ads_sasl_spnego_bind},
374 #ifdef HAVE_GSSAPI
375 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
376 #endif
377 {NULL, NULL}
380 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
382 const char *attrs[] = {"supportedSASLMechanisms", NULL};
383 char **values;
384 ADS_STATUS status;
385 int i, j;
386 void *res;
388 /* get a list of supported SASL mechanisms */
389 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
390 if (!ADS_ERR_OK(status)) return status;
392 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
394 /* try our supported mechanisms in order */
395 for (i=0;sasl_mechanisms[i].name;i++) {
396 /* see if the server supports it */
397 for (j=0;values && values[j];j++) {
398 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
399 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
400 status = sasl_mechanisms[i].fn(ads);
401 ldap_value_free(values);
402 ldap_msgfree(res);
403 return status;
408 ldap_value_free(values);
409 ldap_msgfree(res);
410 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
413 #endif