48873252f082a28b2dedaf908f9b80dc9a7ba3f0
[kai/samba.git] / source3 / libads / sasl.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    ads sasl code
5    Copyright (C) Andrew Tridgell 2001
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 #ifdef HAVE_ADS
25
26 #if USE_CYRUS_SASL
27 /*
28   this is a minimal interact function, just enough for SASL to talk
29   GSSAPI/kerberos to W2K
30   Error handling is a bit of a problem. I can't see how to get Cyrus-sasl
31   to give sensible errors
32 */
33 static int sasl_interact(LDAP *ld,unsigned flags,void *defaults,void *in)
34 {
35         sasl_interact_t *interact = in;
36
37         while (interact->id != SASL_CB_LIST_END) {
38                 interact->result = strdup("");
39                 interact->len = strlen(interact->result);
40                 interact++;
41         }
42         
43         return LDAP_SUCCESS;
44 }
45 #endif
46
47
48 #define MAX_GSS_PASSES 3
49
50 /* this performs a SASL/gssapi bind
51    we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
52    is very dependent on correctly configured DNS whereas
53    this routine is much less fragile
54    see RFC2078 for details
55 */
56 ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
57 {
58         int minor_status;
59         gss_name_t serv_name;
60         gss_buffer_desc input_name;
61         gss_ctx_id_t context_handle;
62         gss_OID mech_type = GSS_C_NULL_OID;
63         gss_buffer_desc output_token, input_token;
64         OM_uint32 ret_flags, conf_state;
65         struct berval cred;
66         struct berval *scred;
67         int i=0;
68         int gss_rc, rc;
69         uint8 *p;
70         uint32 max_msg_size;
71         char *sname;
72         ADS_STATUS status;
73         krb5_principal principal;
74         krb5_context ctx;
75         krb5_enctype enc_types[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL};
76         gss_OID_desc nt_principal = 
77         {10, "\052\206\110\206\367\022\001\002\002\002"};
78
79         /* we need to fetch a service ticket as the ldap user in the
80            servers realm, regardless of our realm */
81         asprintf(&sname, "ldap/%s@%s", ads->ldap_server_name, ads->server_realm);
82         krb5_init_context(&ctx);
83         krb5_set_default_tgs_ktypes(ctx, enc_types);
84         krb5_parse_name(ctx, sname, &principal);
85         free(sname);
86         krb5_free_context(ctx); 
87
88         input_name.value = &principal;
89         input_name.length = sizeof(principal);
90
91         gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
92         if (gss_rc) {
93                 return ADS_ERROR_GSS(gss_rc, minor_status);
94         }
95
96         context_handle = GSS_C_NO_CONTEXT;
97
98         input_token.value = NULL;
99         input_token.length = 0;
100
101         for (i=0; i < MAX_GSS_PASSES; i++) {
102                 gss_rc = gss_init_sec_context(&minor_status,
103                                           GSS_C_NO_CREDENTIAL,
104                                           &context_handle,
105                                           serv_name,
106                                           mech_type,
107                                           GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
108                                           0,
109                                           NULL,
110                                           &input_token,
111                                           NULL,
112                                           &output_token,
113                                           &ret_flags,
114                                           NULL);
115
116                 if (input_token.value) {
117                         gss_release_buffer(&minor_status, &input_token);
118                 }
119
120                 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
121                         status = ADS_ERROR_GSS(gss_rc, minor_status);
122                         goto failed;
123                 }
124
125                 cred.bv_val = output_token.value;
126                 cred.bv_len = output_token.length;
127
128                 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
129                                       &scred);
130                 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
131                         status = ADS_ERROR(rc);
132                         goto failed;
133                 }
134
135                 if (output_token.value) {
136                         gss_release_buffer(&minor_status, &output_token);
137                 }
138
139                 if (scred) {
140                         input_token.value = scred->bv_val;
141                         input_token.length = scred->bv_len;
142                 } else {
143                         input_token.value = NULL;
144                         input_token.length = 0;
145                 }
146
147                 if (gss_rc == 0) break;
148         }
149
150         gss_release_name(&minor_status, &serv_name);
151
152         gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
153                             &conf_state,NULL);
154         if (gss_rc) {
155                 status = ADS_ERROR_GSS(gss_rc, minor_status);
156                 goto failed;
157         }
158
159         gss_release_buffer(&minor_status, &input_token);
160
161         p = (uint8 *)output_token.value;
162
163         max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
164
165         gss_release_buffer(&minor_status, &output_token);
166
167         output_token.value = malloc(strlen(ads->bind_path) + 8);
168         p = output_token.value;
169
170         *p++ = 1; /* no sign or seal */
171         /* choose the same size as the server gave us */
172         *p++ = max_msg_size>>16;
173         *p++ = max_msg_size>>8;
174         *p++ = max_msg_size;
175         snprintf(p, strlen(ads->bind_path)+1, "dn:%s", ads->bind_path);
176         p += strlen(ads->bind_path);
177
178         output_token.length = strlen(ads->bind_path) + 8;
179
180         gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
181                           &output_token, &conf_state,
182                           &input_token);
183         if (gss_rc) {
184                 status = ADS_ERROR_GSS(gss_rc, minor_status);
185                 goto failed;
186         }
187
188         free(output_token.value);
189
190         cred.bv_val = input_token.value;
191         cred.bv_len = input_token.length;
192
193         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
194                               &scred);
195         status = ADS_ERROR(rc);
196
197         gss_release_buffer(&minor_status, &input_token);
198
199 failed:
200         return status;
201 }
202
203 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
204 {
205 #if USE_CYRUS_SASL
206         int rc;
207         rc = ldap_sasl_interactive_bind_s(ads->ld, NULL, NULL, NULL, NULL, 
208                                           LDAP_SASL_QUIET,
209                                           sasl_interact, NULL);
210         return ADS_ERROR(rc);
211 #else
212         return ads_sasl_gssapi_bind(ads);
213 #endif
214 }
215
216 #endif
217