Antti Andreimann <Antti.Andreimann@mail.ee> has done some changes to enable
[tprouty/samba.git] / source / 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                 if (ADS_ERR_OK(status))
202                         return status;
203         }
204 #endif
205
206         /* lets do NTLMSSP ... this has the big advantage that we don't need
207            to sync clocks, and we don't rely on special versions of the krb5 
208            library for HMAC_MD4 encryption */
209         return ads_sasl_spnego_ntlmssp_bind(ads);
210
211 failed:
212         return status;
213 }
214
215 #ifdef HAVE_GSSAPI
216 #define MAX_GSS_PASSES 3
217
218 /* this performs a SASL/gssapi bind
219    we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
220    is very dependent on correctly configured DNS whereas
221    this routine is much less fragile
222    see RFC2078 and RFC2222 for details
223 */
224 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
225 {
226         int minor_status;
227         gss_name_t serv_name;
228         gss_buffer_desc input_name;
229         gss_ctx_id_t context_handle;
230         gss_OID mech_type = GSS_C_NULL_OID;
231         gss_buffer_desc output_token, input_token;
232         OM_uint32 ret_flags, conf_state;
233         struct berval cred;
234         struct berval *scred;
235         int i=0;
236         int gss_rc, rc;
237         uint8 *p;
238         uint32 max_msg_size;
239         char *sname;
240         unsigned sec_layer;
241         ADS_STATUS status;
242         krb5_principal principal;
243         krb5_context ctx;
244         krb5_enctype enc_types[] = {
245 #ifdef ENCTYPE_ARCFOUR_HMAC
246                         ENCTYPE_ARCFOUR_HMAC,
247 #endif
248                         ENCTYPE_DES_CBC_MD5,
249                         ENCTYPE_NULL};
250         gss_OID_desc nt_principal = 
251         {10, "\052\206\110\206\367\022\001\002\002\002"};
252
253         /* we need to fetch a service ticket as the ldap user in the
254            servers realm, regardless of our realm */
255         asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
256         krb5_init_context(&ctx);
257         krb5_set_default_tgs_ktypes(ctx, enc_types);
258         krb5_parse_name(ctx, sname, &principal);
259         free(sname);
260         krb5_free_context(ctx); 
261
262         input_name.value = &principal;
263         input_name.length = sizeof(principal);
264
265         gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
266         if (gss_rc) {
267                 return ADS_ERROR_GSS(gss_rc, minor_status);
268         }
269
270         context_handle = GSS_C_NO_CONTEXT;
271
272         input_token.value = NULL;
273         input_token.length = 0;
274
275         for (i=0; i < MAX_GSS_PASSES; i++) {
276                 gss_rc = gss_init_sec_context(&minor_status,
277                                           GSS_C_NO_CREDENTIAL,
278                                           &context_handle,
279                                           serv_name,
280                                           mech_type,
281                                           GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
282                                           0,
283                                           NULL,
284                                           &input_token,
285                                           NULL,
286                                           &output_token,
287                                           &ret_flags,
288                                           NULL);
289
290                 if (input_token.value) {
291                         gss_release_buffer(&minor_status, &input_token);
292                 }
293
294                 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
295                         status = ADS_ERROR_GSS(gss_rc, minor_status);
296                         goto failed;
297                 }
298
299                 cred.bv_val = output_token.value;
300                 cred.bv_len = output_token.length;
301
302                 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
303                                       &scred);
304                 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
305                         status = ADS_ERROR(rc);
306                         goto failed;
307                 }
308
309                 if (output_token.value) {
310                         gss_release_buffer(&minor_status, &output_token);
311                 }
312
313                 if (scred) {
314                         input_token.value = scred->bv_val;
315                         input_token.length = scred->bv_len;
316                 } else {
317                         input_token.value = NULL;
318                         input_token.length = 0;
319                 }
320
321                 if (gss_rc == 0) break;
322         }
323
324         gss_release_name(&minor_status, &serv_name);
325
326         gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
327                             &conf_state,NULL);
328         if (gss_rc) {
329                 status = ADS_ERROR_GSS(gss_rc, minor_status);
330                 goto failed;
331         }
332
333         gss_release_buffer(&minor_status, &input_token);
334
335         p = (uint8 *)output_token.value;
336
337         file_save("sasl_gssapi.dat", output_token.value, output_token.length);
338
339         max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
340         sec_layer = *p;
341
342         gss_release_buffer(&minor_status, &output_token);
343
344         output_token.value = malloc(strlen(ads->config.bind_path) + 8);
345         p = output_token.value;
346
347         *p++ = 1; /* no sign & seal selection */
348         /* choose the same size as the server gave us */
349         *p++ = max_msg_size>>16;
350         *p++ = max_msg_size>>8;
351         *p++ = max_msg_size;
352         snprintf(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
353         p += strlen(p);
354
355         output_token.length = PTR_DIFF(p, output_token.value);
356
357         gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
358                           &output_token, &conf_state,
359                           &input_token);
360         if (gss_rc) {
361                 status = ADS_ERROR_GSS(gss_rc, minor_status);
362                 goto failed;
363         }
364
365         free(output_token.value);
366
367         cred.bv_val = input_token.value;
368         cred.bv_len = input_token.length;
369
370         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
371                               &scred);
372         status = ADS_ERROR(rc);
373
374         gss_release_buffer(&minor_status, &input_token);
375
376 failed:
377         return status;
378 }
379 #endif
380
381 /* mapping between SASL mechanisms and functions */
382 static struct {
383         const char *name;
384         ADS_STATUS (*fn)(ADS_STRUCT *);
385 } sasl_mechanisms[] = {
386         {"GSS-SPNEGO", ads_sasl_spnego_bind},
387 #ifdef HAVE_GSSAPI
388         {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
389 #endif
390         {NULL, NULL}
391 };
392
393 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
394 {
395         const char *attrs[] = {"supportedSASLMechanisms", NULL};
396         char **values;
397         ADS_STATUS status;
398         int i, j;
399         void *res;
400
401         /* get a list of supported SASL mechanisms */
402         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
403         if (!ADS_ERR_OK(status)) return status;
404
405         values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
406
407         /* try our supported mechanisms in order */
408         for (i=0;sasl_mechanisms[i].name;i++) {
409                 /* see if the server supports it */
410                 for (j=0;values && values[j];j++) {
411                         if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
412                                 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
413                                 status = sasl_mechanisms[i].fn(ads);
414                                 ldap_value_free(values);
415                                 ldap_msgfree(res);
416                                 return status;
417                         }
418                 }
419         }
420
421         ldap_value_free(values);
422         ldap_msgfree(res);
423         return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
424 }
425
426 #endif
427