make sure that if kerberos fails we can fall back on NTLMSSP for SASL
[kai/samba.git] / source3 / libads / sasl.c
1 /* 
2    Unix SMB/CIFS implementation.
3    ads sasl code
4    Copyright (C) Andrew Tridgell 2001
5    
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.
10    
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.
15    
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.
19 */
20
21 #include "includes.h"
22
23 #ifdef HAVE_LDAP
24
25 /* 
26    perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
27    we fit on one socket??)
28 */
29 static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
30 {
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;
41
42         if (!ads->auth.password) {
43                 /* No password, don't segfault below... */
44                 return ADS_ERROR_NT(NT_STATUS_LOGON_FAILURE);
45         }
46
47         neg_flags = NTLMSSP_NEGOTIATE_UNICODE | 
48                 NTLMSSP_NEGOTIATE_128 | 
49                 NTLMSSP_NEGOTIATE_NTLM;
50
51         memset(sess_key, 0, 16);
52
53         /* generate the ntlmssp negotiate packet */
54         msrpc_gen(&blob, "CddB",
55                   "NTLMSSP",
56                   NTLMSSP_NEGOTIATE,
57                   neg_flags,
58                   sess_key, 16);
59
60         /* and wrap it in a SPNEGO wrapper */
61         msg1 = gen_negTokenTarg(mechs, blob);
62         data_blob_free(&blob);
63
64         cred.bv_val = msg1.data;
65         cred.bv_len = msg1.length;
66
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;
71         }
72
73         blob = data_blob(scred->bv_val, scred->bv_len);
74
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;
80         }
81
82         data_blob_free(&blob);
83
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);
88
89         data_blob_free(&chal1);
90         data_blob_free(&chal2);
91
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);
103
104         /* wrap it in SPNEGO */
105         auth = spnego_gen_auth(blob);
106
107         data_blob_free(&blob);
108
109         /* now send the auth packet and we should be done */
110         cred.bv_val = auth.data;
111         cred.bv_len = auth.length;
112
113         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
114
115         return ADS_ERROR(rc);
116
117 failed:
118         return status;
119 }
120
121 /* 
122    perform a LDAP/SASL/SPNEGO/KRB5 bind
123 */
124 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
125 {
126         DATA_BLOB blob;
127         struct berval cred, *scred;
128         int rc;
129
130         blob = spnego_gen_negTokenTarg(principal, ads->auth.time_offset);
131
132         if (!blob.data) {
133                 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
134         }
135
136         /* now send the auth packet and we should be done */
137         cred.bv_val = blob.data;
138         cred.bv_len = blob.length;
139
140         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
141
142         data_blob_free(&blob);
143
144         return ADS_ERROR(rc);
145 }
146
147 /* 
148    this performs a SASL/SPNEGO bind
149 */
150 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
151 {
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;
159
160         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
161
162         if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
163                 status = ADS_ERROR(rc);
164                 goto failed;
165         }
166
167         blob = data_blob(scred->bv_val, scred->bv_len);
168
169 #if 0
170         file_save("sasl_spnego.dat", blob.data, blob.length);
171 #endif
172
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;
179         }
180         data_blob_free(&blob);
181
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;
188                 }
189                 free(OIDs[i]);
190         }
191         DEBUG(3,("got principal=%s\n", principal));
192
193 #ifdef HAVE_KRB5
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))
198                         return status;
199                 if (ads_kinit_password(ads) == 0) {
200                         status = ads_sasl_spnego_krb5_bind(ads, principal);
201                 }
202                 if (ADS_ERR_OK(status))
203                         return status;
204         }
205 #endif
206
207         /* lets do NTLMSSP ... this has the big advantage that we don't need
208            to sync clocks, and we don't rely on special versions of the krb5 
209            library for HMAC_MD4 encryption */
210         return ads_sasl_spnego_ntlmssp_bind(ads);
211
212 failed:
213         return status;
214 }
215
216 #ifdef HAVE_GSSAPI
217 #define MAX_GSS_PASSES 3
218
219 /* this performs a SASL/gssapi bind
220    we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
221    is very dependent on correctly configured DNS whereas
222    this routine is much less fragile
223    see RFC2078 and RFC2222 for details
224 */
225 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
226 {
227         int minor_status;
228         gss_name_t serv_name;
229         gss_buffer_desc input_name;
230         gss_ctx_id_t context_handle;
231         gss_OID mech_type = GSS_C_NULL_OID;
232         gss_buffer_desc output_token, input_token;
233         OM_uint32 ret_flags, conf_state;
234         struct berval cred;
235         struct berval *scred;
236         int i=0;
237         int gss_rc, rc;
238         uint8 *p;
239         uint32 max_msg_size;
240         char *sname;
241         unsigned sec_layer;
242         ADS_STATUS status;
243         krb5_principal principal;
244         krb5_context ctx;
245         krb5_enctype enc_types[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL};
246         gss_OID_desc nt_principal = 
247         {10, "\052\206\110\206\367\022\001\002\002\002"};
248
249         /* we need to fetch a service ticket as the ldap user in the
250            servers realm, regardless of our realm */
251         asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
252         krb5_init_context(&ctx);
253         krb5_set_default_tgs_ktypes(ctx, enc_types);
254         krb5_parse_name(ctx, sname, &principal);
255         free(sname);
256         krb5_free_context(ctx); 
257
258         input_name.value = &principal;
259         input_name.length = sizeof(principal);
260
261         gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
262         if (gss_rc) {
263                 return ADS_ERROR_GSS(gss_rc, minor_status);
264         }
265
266         context_handle = GSS_C_NO_CONTEXT;
267
268         input_token.value = NULL;
269         input_token.length = 0;
270
271         for (i=0; i < MAX_GSS_PASSES; i++) {
272                 gss_rc = gss_init_sec_context(&minor_status,
273                                           GSS_C_NO_CREDENTIAL,
274                                           &context_handle,
275                                           serv_name,
276                                           mech_type,
277                                           GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
278                                           0,
279                                           NULL,
280                                           &input_token,
281                                           NULL,
282                                           &output_token,
283                                           &ret_flags,
284                                           NULL);
285
286                 if (input_token.value) {
287                         gss_release_buffer(&minor_status, &input_token);
288                 }
289
290                 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
291                         status = ADS_ERROR_GSS(gss_rc, minor_status);
292                         goto failed;
293                 }
294
295                 cred.bv_val = output_token.value;
296                 cred.bv_len = output_token.length;
297
298                 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
299                                       &scred);
300                 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
301                         status = ADS_ERROR(rc);
302                         goto failed;
303                 }
304
305                 if (output_token.value) {
306                         gss_release_buffer(&minor_status, &output_token);
307                 }
308
309                 if (scred) {
310                         input_token.value = scred->bv_val;
311                         input_token.length = scred->bv_len;
312                 } else {
313                         input_token.value = NULL;
314                         input_token.length = 0;
315                 }
316
317                 if (gss_rc == 0) break;
318         }
319
320         gss_release_name(&minor_status, &serv_name);
321
322         gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
323                             &conf_state,NULL);
324         if (gss_rc) {
325                 status = ADS_ERROR_GSS(gss_rc, minor_status);
326                 goto failed;
327         }
328
329         gss_release_buffer(&minor_status, &input_token);
330
331         p = (uint8 *)output_token.value;
332
333         file_save("sasl_gssapi.dat", output_token.value, output_token.length);
334
335         max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
336         sec_layer = *p;
337
338         gss_release_buffer(&minor_status, &output_token);
339
340         output_token.value = malloc(strlen(ads->config.bind_path) + 8);
341         p = output_token.value;
342
343         *p++ = 1; /* no sign & seal selection */
344         /* choose the same size as the server gave us */
345         *p++ = max_msg_size>>16;
346         *p++ = max_msg_size>>8;
347         *p++ = max_msg_size;
348         snprintf(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
349         p += strlen(p);
350
351         output_token.length = PTR_DIFF(p, output_token.value);
352
353         gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
354                           &output_token, &conf_state,
355                           &input_token);
356         if (gss_rc) {
357                 status = ADS_ERROR_GSS(gss_rc, minor_status);
358                 goto failed;
359         }
360
361         free(output_token.value);
362
363         cred.bv_val = input_token.value;
364         cred.bv_len = input_token.length;
365
366         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
367                               &scred);
368         status = ADS_ERROR(rc);
369
370         gss_release_buffer(&minor_status, &input_token);
371
372 failed:
373         return status;
374 }
375 #endif
376
377 /* mapping between SASL mechanisms and functions */
378 static struct {
379         const char *name;
380         ADS_STATUS (*fn)(ADS_STRUCT *);
381 } sasl_mechanisms[] = {
382         {"GSS-SPNEGO", ads_sasl_spnego_bind},
383 #ifdef HAVE_GSSAPI
384         {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
385 #endif
386         {NULL, NULL}
387 };
388
389 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
390 {
391         const char *attrs[] = {"supportedSASLMechanisms", NULL};
392         char **values;
393         ADS_STATUS status;
394         int i, j;
395         void *res;
396
397         /* get a list of supported SASL mechanisms */
398         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
399         if (!ADS_ERR_OK(status)) return status;
400
401         values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
402
403         /* try our supported mechanisms in order */
404         for (i=0;sasl_mechanisms[i].name;i++) {
405                 /* see if the server supports it */
406                 for (j=0;values && values[j];j++) {
407                         if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
408                                 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
409                                 status = sasl_mechanisms[i].fn(ads);
410                                 ldap_value_free(values);
411                                 ldap_msgfree(res);
412                                 return status;
413                         }
414                 }
415         }
416
417         ldap_value_free(values);
418         ldap_msgfree(res);
419         return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
420 }
421
422 #endif
423