source: branches/samba-3.0/source/libaddns/dnsgss.c@ 929

Last change on this file since 929 was 22, checked in by Yuri Dario, 18 years ago

Source code upgrade to 3.0.25pre2.

File size: 9.3 KB
Line 
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, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 02110-1301 USA
25*/
26
27#include "dns.h"
28#include <ctype.h>
29
30
31#ifdef HAVE_GSSAPI_SUPPORT
32
33/*********************************************************************
34*********************************************************************/
35
36static int strupr( char *szDomainName )
37{
38 if ( !szDomainName ) {
39 return ( 0 );
40 }
41 while ( *szDomainName != '\0' ) {
42 *szDomainName = toupper( *szDomainName );
43 szDomainName++;
44 }
45 return ( 0 );
46}
47
48#if 0
49/*********************************************************************
50*********************************************************************/
51
52static 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
75void 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
82static 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_SEQUENCE_FLAG | GSS_C_CONF_FLAG |
105 GSS_C_INTEG_FLAG | GSS_C_DELEG_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_additionals != 1) ||
179 (resp->num_answers == 0) ||
180 (resp->answers[0]->type != QTYPE_TKEY)) {
181 err = ERROR_DNS_INVALID_MESSAGE;
182 goto error;
183 }
184
185 err = dns_unmarshall_tkey_record(
186 mem_ctx, resp->answers[0], &tkey);
187 if (!ERR_DNS_IS_OK(err)) goto error;
188
189 input_desc.length = tkey->key_length;
190 input_desc.value = talloc_move(mem_ctx, &tkey->key);
191
192 input_ptr = &input_desc;
193
194 TALLOC_FREE(buf);
195 }
196
197 } while ( major == GSS_S_CONTINUE_NEEDED );
198
199 /* If we arrive here, we have a valid security context */
200
201 err = ERROR_DNS_SUCCESS;
202
203 error:
204
205 return err;
206}
207
208DNS_ERROR dns_negotiate_sec_ctx( const char *target_realm,
209 const char *servername,
210 const char *keyname,
211 gss_ctx_id_t *gss_ctx,
212 enum dns_ServerType srv_type )
213{
214 OM_uint32 major, minor;
215
216 char *upcaserealm, *targetname;
217 DNS_ERROR err;
218
219 gss_buffer_desc input_name;
220 struct dns_connection *conn;
221
222 gss_name_t targ_name;
223
224 krb5_principal host_principal;
225 krb5_context krb_ctx = NULL;
226
227 gss_OID_desc nt_host_oid_desc =
228 { 10, (char *)"\052\206\110\206\367\022\001\002\002\002" };
229
230 TALLOC_CTX *mem_ctx;
231
232 if (!(mem_ctx = talloc_init("dns_negotiate_sec_ctx"))) {
233 return ERROR_DNS_NO_MEMORY;
234 }
235
236 err = dns_open_connection( servername, DNS_TCP, mem_ctx, &conn );
237 if (!ERR_DNS_IS_OK(err)) goto error;
238
239 if (!(upcaserealm = talloc_strdup(mem_ctx, target_realm))) {
240 err = ERROR_DNS_NO_MEMORY;
241 goto error;
242 }
243
244 strupr(upcaserealm);
245
246 if (!(targetname = talloc_asprintf(mem_ctx, "dns/%s@%s",
247 servername, upcaserealm))) {
248 err = ERROR_DNS_NO_MEMORY;
249 goto error;
250 }
251
252 krb5_init_context( &krb_ctx );
253 krb5_parse_name( krb_ctx, targetname, &host_principal );
254
255 /* don't free the principal until after you call
256 gss_release_name() or else you'll get a segv
257 as the krb5_copy_principal() does a structure
258 copy and not a deep copy. --jerry*/
259
260 input_name.value = &host_principal;
261 input_name.length = sizeof( host_principal );
262
263 major = gss_import_name( &minor, &input_name,
264 &nt_host_oid_desc, &targ_name );
265
266 if (major) {
267 krb5_free_principal( krb_ctx, host_principal );
268 krb5_free_context( krb_ctx );
269 err = ERROR_DNS_GSS_ERROR;
270 goto error;
271 }
272
273 err = dns_negotiate_gss_ctx_int(mem_ctx, conn, keyname,
274 targ_name, gss_ctx, srv_type );
275
276 gss_release_name( &minor, &targ_name );
277
278 /* now we can free the principal */
279
280 krb5_free_principal( krb_ctx, host_principal );
281 krb5_free_context( krb_ctx );
282
283 error:
284 TALLOC_FREE(mem_ctx);
285
286 return err;
287}
288
289DNS_ERROR dns_sign_update(struct dns_update_request *req,
290 gss_ctx_id_t gss_ctx,
291 const char *keyname,
292 const char *algorithmname,
293 time_t time_signed, uint16 fudge)
294{
295 struct dns_buffer *buf;
296 DNS_ERROR err;
297 struct dns_domain_name *key, *algorithm;
298 struct gss_buffer_desc_struct msg, mic;
299 OM_uint32 major, minor;
300 struct dns_rrec *rec;
301
302 err = dns_marshall_update_request(req, req, &buf);
303 if (!ERR_DNS_IS_OK(err)) return err;
304
305 err = dns_domain_name_from_string(buf, keyname, &key);
306 if (!ERR_DNS_IS_OK(err)) goto error;
307
308 err = dns_domain_name_from_string(buf, algorithmname, &algorithm);
309 if (!ERR_DNS_IS_OK(err)) goto error;
310
311 dns_marshall_domain_name(buf, key);
312 dns_marshall_uint16(buf, DNS_CLASS_ANY);
313 dns_marshall_uint32(buf, 0); /* TTL */
314 dns_marshall_domain_name(buf, algorithm);
315 dns_marshall_uint16(buf, 0); /* Time prefix for 48-bit time_t */
316 dns_marshall_uint32(buf, time_signed);
317 dns_marshall_uint16(buf, fudge);
318 dns_marshall_uint16(buf, 0); /* error */
319 dns_marshall_uint16(buf, 0); /* other len */
320
321 err = buf->error;
322 if (!ERR_DNS_IS_OK(buf->error)) goto error;
323
324 msg.value = (void *)buf->data;
325 msg.length = buf->offset;
326
327 major = gss_get_mic(&minor, gss_ctx, 0, &msg, &mic);
328 if (major != 0) {
329 err = ERROR_DNS_GSS_ERROR;
330 goto error;
331 }
332
333 if (mic.length > 0xffff) {
334 gss_release_buffer(&minor, &mic);
335 err = ERROR_DNS_GSS_ERROR;
336 goto error;
337 }
338
339 err = dns_create_tsig_record(buf, keyname, algorithmname, time_signed,
340 fudge, mic.length, (uint8 *)mic.value,
341 req->id, 0, &rec);
342 gss_release_buffer(&minor, &mic);
343 if (!ERR_DNS_IS_OK(err)) goto error;
344
345 err = dns_add_rrec(req, rec, &req->num_additionals, &req->additionals);
346
347 error:
348 TALLOC_FREE(buf);
349 return err;
350}
351
352#endif /* HAVE_GSSAPI_SUPPORT */
Note: See TracBrowser for help on using the repository browser.