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