source3/libaddns: remove pointless check for resp->num_additionals != 1
[samba.git] / source3 / libaddns / dnsgss.c
1 /*
2   Public Interface file for Linux DNS client library implementation
3
4   Copyright (C) 2006 Krishna Ganugapati <krishnag@centeris.com>
5   Copyright (C) 2006 Gerald Carter <jerry@samba.org>
6
7      ** NOTE! The following LGPL license applies to the libaddns
8      ** library. This does NOT imply that all of Samba is released
9      ** under the LGPL
10
11   This library is free software; you can redistribute it and/or
12   modify it under the terms of the GNU Lesser General Public
13   License as published by the Free Software Foundation; either
14   version 2.1 of the License, or (at your option) any later version.
15
16   This library is distributed in the hope that it will be useful,
17   but WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19   Lesser General Public License for more details.
20
21   You should have received a copy of the GNU Lesser General Public
22   License along with this library; if not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "dns.h"
26 #include <ctype.h>
27
28
29 #ifdef HAVE_GSSAPI_SUPPORT
30
31 /*********************************************************************
32 *********************************************************************/
33
34 #ifndef HAVE_STRUPR
35 static int strupr( char *szDomainName )
36 {
37         if ( !szDomainName ) {
38                 return ( 0 );
39         }
40         while ( *szDomainName != '\0' ) {
41                 *szDomainName = toupper( *szDomainName );
42                 szDomainName++;
43         }
44         return ( 0 );
45 }
46 #endif
47
48 #if 0
49 /*********************************************************************
50 *********************************************************************/
51
52 static void display_status_1( const char *m, OM_uint32 code, int type )
53 {
54         OM_uint32 maj_stat, min_stat;
55         gss_buffer_desc msg;
56         OM_uint32 msg_ctx;
57
58         msg_ctx = 0;
59         while ( 1 ) {
60                 maj_stat = gss_display_status( &min_stat, code,
61                                                type, GSS_C_NULL_OID,
62                                                &msg_ctx, &msg );
63                 fprintf( stdout, "GSS-API error %s: %s\n", m,
64                          ( char * ) msg.value );
65                 ( void ) gss_release_buffer( &min_stat, &msg );
66
67                 if ( !msg_ctx )
68                         break;
69         }
70 }
71
72 /*********************************************************************
73 *********************************************************************/
74
75 void display_status( const char *msg, OM_uint32 maj_stat, OM_uint32 min_stat )
76 {
77         display_status_1( msg, maj_stat, GSS_C_GSS_CODE );
78         display_status_1( msg, min_stat, GSS_C_MECH_CODE );
79 }
80 #endif
81
82 static DNS_ERROR dns_negotiate_gss_ctx_int( TALLOC_CTX *mem_ctx,
83                                             struct dns_connection *conn,
84                                             const char *keyname,
85                                             const gss_name_t target_name,
86                                             gss_ctx_id_t *ctx, 
87                                             enum dns_ServerType srv_type )
88 {
89         struct gss_buffer_desc_struct input_desc, *input_ptr, output_desc;
90         OM_uint32 major, minor;
91         OM_uint32 ret_flags;
92         DNS_ERROR err;
93
94         gss_OID_desc krb5_oid_desc =
95                 { 9, (char *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
96
97         *ctx = GSS_C_NO_CONTEXT;
98         input_ptr = NULL;
99
100         do {
101                 major = gss_init_sec_context(
102                         &minor, NULL, ctx, target_name, &krb5_oid_desc,
103                         GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG |
104                         GSS_C_CONF_FLAG |
105                         GSS_C_INTEG_FLAG,
106                         0, NULL, input_ptr, NULL, &output_desc,
107                         &ret_flags, NULL );
108
109                 if (input_ptr != NULL) {
110                         TALLOC_FREE(input_desc.value);
111                 }
112
113                 if (output_desc.length != 0) {
114
115                         struct dns_request *req;
116                         struct dns_rrec *rec;
117                         struct dns_buffer *buf;
118
119                         time_t t = time(NULL);
120
121                         err = dns_create_query(mem_ctx, keyname, QTYPE_TKEY,
122                                                DNS_CLASS_IN, &req);
123                         if (!ERR_DNS_IS_OK(err)) goto error;
124
125                         err = dns_create_tkey_record(
126                                 req, keyname, "gss.microsoft.com", t,
127                                 t + 86400, DNS_TKEY_MODE_GSSAPI, 0,
128                                 output_desc.length, (uint8 *)output_desc.value,
129                                 &rec );
130                         if (!ERR_DNS_IS_OK(err)) goto error;
131
132                         /* Windows 2000 DNS is broken and requires the
133                            TKEY payload in the Answer section instead
134                            of the Additional seciton like Windows 2003 */
135
136                         if ( srv_type == DNS_SRV_WIN2000 ) {
137                                 err = dns_add_rrec(req, rec, &req->num_answers,
138                                                    &req->answers);
139                         } else {
140                                 err = dns_add_rrec(req, rec, &req->num_additionals,
141                                                    &req->additionals);
142                         }
143                         
144                         if (!ERR_DNS_IS_OK(err)) goto error;
145
146                         err = dns_marshall_request(req, req, &buf);
147                         if (!ERR_DNS_IS_OK(err)) goto error;
148
149                         err = dns_send(conn, buf);
150                         if (!ERR_DNS_IS_OK(err)) goto error;
151
152                         TALLOC_FREE(req);
153                 }
154
155                 gss_release_buffer(&minor, &output_desc);
156
157                 if ((major != GSS_S_COMPLETE) &&
158                     (major != GSS_S_CONTINUE_NEEDED)) {
159                         return ERROR_DNS_GSS_ERROR;
160                 }
161
162                 if (major == GSS_S_CONTINUE_NEEDED) {
163
164                         struct dns_request *resp;
165                         struct dns_buffer *buf;
166                         struct dns_tkey_record *tkey;
167
168                         err = dns_receive(mem_ctx, conn, &buf);
169                         if (!ERR_DNS_IS_OK(err)) goto error;
170
171                         err = dns_unmarshall_request(buf, buf, &resp);
172                         if (!ERR_DNS_IS_OK(err)) goto error;
173
174                         /*
175                          * TODO: Compare id and keyname
176                          */
177                         
178                         if ((resp->num_answers == 0) ||
179                             (resp->answers[0]->type != QTYPE_TKEY)) {
180                                 err = ERROR_DNS_INVALID_MESSAGE;
181                                 goto error;
182                         }
183
184                         err = dns_unmarshall_tkey_record(
185                                 mem_ctx, resp->answers[0], &tkey);
186                         if (!ERR_DNS_IS_OK(err)) goto error;
187
188                         input_desc.length = tkey->key_length;
189                         input_desc.value = talloc_move(mem_ctx, &tkey->key);
190
191                         input_ptr = &input_desc;
192
193                         TALLOC_FREE(buf);
194                 }
195
196         } while ( major == GSS_S_CONTINUE_NEEDED );
197
198         /* If we arrive here, we have a valid security context */
199
200         err = ERROR_DNS_SUCCESS;
201
202       error:
203
204         return err;
205 }
206
207 DNS_ERROR dns_negotiate_sec_ctx( const char *target_realm,
208                                  const char *servername,
209                                  const char *keyname,
210                                  gss_ctx_id_t *gss_ctx,
211                                  enum dns_ServerType srv_type )
212 {
213         OM_uint32 major, minor;
214
215         char *upcaserealm, *targetname;
216         DNS_ERROR err;
217
218         gss_buffer_desc input_name;
219         struct dns_connection *conn;
220
221         gss_name_t targ_name;
222
223         gss_OID_desc nt_host_oid_desc =
224                 {10, (char *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01"};
225
226         TALLOC_CTX *mem_ctx;
227
228         if (!(mem_ctx = talloc_init("dns_negotiate_sec_ctx"))) {
229                 return ERROR_DNS_NO_MEMORY;
230         }
231
232         err = dns_open_connection( servername, DNS_TCP, mem_ctx, &conn );
233         if (!ERR_DNS_IS_OK(err)) goto error;
234
235         if (!(upcaserealm = talloc_strdup(mem_ctx, target_realm))) {
236                 err = ERROR_DNS_NO_MEMORY;
237                 goto error;
238         }
239
240         strupr(upcaserealm);
241
242         if (!(targetname = talloc_asprintf(mem_ctx, "dns/%s@%s",
243                                            servername, upcaserealm))) {
244                 err = ERROR_DNS_NO_MEMORY;
245                 goto error;
246         }
247
248         input_name.value = targetname;
249         input_name.length = strlen(targetname);
250
251         major = gss_import_name( &minor, &input_name,
252                                  &nt_host_oid_desc, &targ_name );
253
254         if (major) {
255                 err = ERROR_DNS_GSS_ERROR;
256                 goto error;
257         }
258
259         err = dns_negotiate_gss_ctx_int(mem_ctx, conn, keyname, 
260                                         targ_name, gss_ctx, srv_type );
261         
262         gss_release_name( &minor, &targ_name );
263
264  error:
265         TALLOC_FREE(mem_ctx);
266
267         return err;
268 }
269
270 DNS_ERROR dns_sign_update(struct dns_update_request *req,
271                           gss_ctx_id_t gss_ctx,
272                           const char *keyname,
273                           const char *algorithmname,
274                           time_t time_signed, uint16 fudge)
275 {
276         struct dns_buffer *buf;
277         DNS_ERROR err;
278         struct dns_domain_name *key, *algorithm;
279         struct gss_buffer_desc_struct msg, mic;
280         OM_uint32 major, minor;
281         struct dns_rrec *rec;
282
283         err = dns_marshall_update_request(req, req, &buf);
284         if (!ERR_DNS_IS_OK(err)) return err;
285
286         err = dns_domain_name_from_string(buf, keyname, &key);
287         if (!ERR_DNS_IS_OK(err)) goto error;
288
289         err = dns_domain_name_from_string(buf, algorithmname, &algorithm);
290         if (!ERR_DNS_IS_OK(err)) goto error;
291
292         dns_marshall_domain_name(buf, key);
293         dns_marshall_uint16(buf, DNS_CLASS_ANY);
294         dns_marshall_uint32(buf, 0); /* TTL */
295         dns_marshall_domain_name(buf, algorithm);
296         dns_marshall_uint16(buf, 0); /* Time prefix for 48-bit time_t */
297         dns_marshall_uint32(buf, time_signed);
298         dns_marshall_uint16(buf, fudge);
299         dns_marshall_uint16(buf, 0); /* error */
300         dns_marshall_uint16(buf, 0); /* other len */
301
302         err = buf->error;
303         if (!ERR_DNS_IS_OK(buf->error)) goto error;
304
305         msg.value = (void *)buf->data;
306         msg.length = buf->offset;
307
308         major = gss_get_mic(&minor, gss_ctx, 0, &msg, &mic);
309         if (major != 0) {
310                 err = ERROR_DNS_GSS_ERROR;
311                 goto error;
312         }
313
314         if (mic.length > 0xffff) {
315                 gss_release_buffer(&minor, &mic);
316                 err = ERROR_DNS_GSS_ERROR;
317                 goto error;
318         }
319
320         err = dns_create_tsig_record(buf, keyname, algorithmname, time_signed,
321                                      fudge, mic.length, (uint8 *)mic.value,
322                                      req->id, 0, &rec);
323         gss_release_buffer(&minor, &mic);
324         if (!ERR_DNS_IS_OK(err)) goto error;
325
326         err = dns_add_rrec(req, rec, &req->num_additionals, &req->additionals);
327
328  error:
329         TALLOC_FREE(buf);
330         return err;
331 }
332
333 #endif  /* HAVE_GSSAPI_SUPPORT */