import HEAD into svn+ssh://svn.samba.org/home/svn/samba/trunk
[metze/old/v3-2-winbind-ndr.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 = (char *)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 = (char *)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         DATA_BLOB session_key;
128         int rc;
129
130         rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key);
131
132         if (rc) {
133                 return ADS_ERROR_KRB5(rc);
134         }
135
136         /* now send the auth packet and we should be done */
137         cred.bv_val = (char *)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         data_blob_free(&session_key);
144
145         return ADS_ERROR(rc);
146 }
147
148 /* 
149    this performs a SASL/SPNEGO bind
150 */
151 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
152 {
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;
160
161         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
162
163         if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
164                 status = ADS_ERROR(rc);
165                 goto failed;
166         }
167
168         blob = data_blob(scred->bv_val, scred->bv_len);
169
170         ber_bvfree(scred);
171
172 #if 0
173         file_save("sasl_spnego.dat", blob.data, blob.length);
174 #endif
175
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;
182         }
183         data_blob_free(&blob);
184
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;
191                 }
192                 free(OIDs[i]);
193         }
194         DEBUG(3,("got principal=%s\n", principal));
195
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;
202
203                 status = ADS_ERROR_KRB5(ads_kinit_password(ads)); 
204
205                 if (ADS_ERR_OK(status)) {
206                         status = ads_sasl_spnego_krb5_bind(ads, principal);
207                 }
208
209                 /* only fallback to NTLMSSP if allowed */
210                 if (ADS_ERR_OK(status) || 
211                     !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
212                         return status;
213                 }
214         }
215 #endif
216
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);
221
222 failed:
223         return status;
224 }
225
226 #ifdef HAVE_GSSAPI
227 #define MAX_GSS_PASSES 3
228
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
234 */
235 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
236 {
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"};
263
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); 
272
273         input_name.value = &principal;
274         input_name.length = sizeof(principal);
275
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);
279         }
280
281         context_handle = GSS_C_NO_CONTEXT;
282
283         input_token.value = NULL;
284         input_token.length = 0;
285
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,
293                                           0,
294                                           NULL,
295                                           &input_token,
296                                           NULL,
297                                           &output_token,
298                                           &ret_flags,
299                                           NULL);
300
301                 if (input_token.value) {
302                         gss_release_buffer(&minor_status, &input_token);
303                 }
304
305                 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
306                         status = ADS_ERROR_GSS(gss_rc, minor_status);
307                         goto failed;
308                 }
309
310                 cred.bv_val = output_token.value;
311                 cred.bv_len = output_token.length;
312
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;
318                 }
319
320                 if (output_token.value) {
321                         gss_release_buffer(&minor_status, &output_token);
322                 }
323
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;
330                 }
331
332                 if (gss_rc == 0) break;
333         }
334
335         gss_release_name(&minor_status, &serv_name);
336
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;
342         }
343
344         gss_release_buffer(&minor_status, &input_token);
345
346         p = (uint8 *)output_token.value;
347
348         file_save("sasl_gssapi.dat", output_token.value, output_token.length);
349
350         max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
351         sec_layer = *p;
352
353         gss_release_buffer(&minor_status, &output_token);
354
355         output_token.value = malloc(strlen(ads->config.bind_path) + 8);
356         p = output_token.value;
357
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);
365
366         output_token.length = PTR_DIFF(p, output_token.value);
367
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;
374         }
375
376         free(output_token.value);
377
378         cred.bv_val = input_token.value;
379         cred.bv_len = input_token.length;
380
381         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
382                               &scred);
383         status = ADS_ERROR(rc);
384
385         gss_release_buffer(&minor_status, &input_token);
386
387 failed:
388         return status;
389 }
390 #endif
391
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}
402 };
403
404 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
405 {
406         const char *attrs[] = {"supportedSASLMechanisms", NULL};
407         char **values;
408         ADS_STATUS status;
409         int i, j;
410         void *res;
411
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;
415
416         values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
417
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;
428                         }
429                 }
430         }
431
432         ldap_value_free(values);
433         ldap_msgfree(res);
434         return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
435 }
436
437 #endif
438