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
30
31 /*********************************************************************
32 *********************************************************************/
33
34 #ifndef HAVE_STRUPR
strupr(char * szDomainName)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
dns_negotiate_gss_ctx_int(TALLOC_CTX * mem_ctx,struct dns_connection * conn,const char * keyname,const gss_name_t target_name,gss_ctx_id_t * ctx,enum dns_ServerType srv_type)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 struct dns_request *req = NULL;
93 struct dns_buffer *buf = NULL;
94 DNS_ERROR err;
95
96 gss_OID_desc krb5_oid_desc =
97 { 9, discard_const("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02") };
98
99 *ctx = GSS_C_NO_CONTEXT;
100 input_ptr = NULL;
101
102 do {
103 major = gss_init_sec_context(
104 &minor, NULL, ctx, target_name, &krb5_oid_desc,
105 GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG |
106 GSS_C_CONF_FLAG |
107 GSS_C_INTEG_FLAG,
108 0, NULL, input_ptr, NULL, &output_desc,
109 &ret_flags, NULL );
110
111 if (input_ptr != NULL) {
112 TALLOC_FREE(input_desc.value);
113 }
114
115 if (output_desc.length != 0) {
116
117 struct dns_rrec *rec;
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_t *)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 section 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(mem_ctx, 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(buf);
153 TALLOC_FREE(req);
154 }
155
156 gss_release_buffer(&minor, &output_desc);
157
158 if ((major != GSS_S_COMPLETE) &&
159 (major != GSS_S_CONTINUE_NEEDED)) {
160 return ERROR_DNS_GSS_ERROR;
161 }
162
163 if (major == GSS_S_CONTINUE_NEEDED) {
164
165 struct dns_request *resp;
166 struct dns_tkey_record *tkey;
167 struct dns_rrec *tkey_answer = NULL;
168 uint16_t i;
169
170 err = dns_receive(mem_ctx, conn, &buf);
171 if (!ERR_DNS_IS_OK(err)) goto error;
172
173 err = dns_unmarshall_request(buf, buf, &resp);
174 if (!ERR_DNS_IS_OK(err)) goto error;
175
176 /*
177 * TODO: Compare id and keyname
178 */
179
180 for (i=0; i < resp->num_answers; i++) {
181 if (resp->answers[i]->type != QTYPE_TKEY) {
182 continue;
183 }
184
185 tkey_answer = resp->answers[i];
186 }
187
188 if (tkey_answer == NULL) {
189 err = ERROR_DNS_INVALID_MESSAGE;
190 goto error;
191 }
192
193 err = dns_unmarshall_tkey_record(
194 mem_ctx, resp->answers[0], &tkey);
195 if (!ERR_DNS_IS_OK(err)) goto error;
196
197 input_desc.length = tkey->key_length;
198 input_desc.value = talloc_move(mem_ctx, &tkey->key);
199
200 input_ptr = &input_desc;
201
202 TALLOC_FREE(buf);
203 }
204
205 } while ( major == GSS_S_CONTINUE_NEEDED );
206
207 /* If we arrive here, we have a valid security context */
208
209 err = ERROR_DNS_SUCCESS;
210
211 error:
212
213 TALLOC_FREE(buf);
214 TALLOC_FREE(req);
215 return err;
216 }
217
dns_negotiate_sec_ctx(const char * target_realm,const char * servername,const char * keyname,gss_ctx_id_t * gss_ctx,enum dns_ServerType srv_type)218 DNS_ERROR dns_negotiate_sec_ctx( const char *target_realm,
219 const char *servername,
220 const char *keyname,
221 gss_ctx_id_t *gss_ctx,
222 enum dns_ServerType srv_type )
223 {
224 OM_uint32 major, minor;
225
226 char *upcaserealm, *targetname;
227 DNS_ERROR err;
228
229 gss_buffer_desc input_name;
230 struct dns_connection *conn;
231
232 gss_name_t targ_name;
233
234 gss_OID_desc nt_host_oid_desc =
235 {10, discard_const("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01")};
236
237 TALLOC_CTX *mem_ctx;
238
239 if (!(mem_ctx = talloc_init("dns_negotiate_sec_ctx"))) {
240 return ERROR_DNS_NO_MEMORY;
241 }
242
243 err = dns_open_connection( servername, DNS_TCP, mem_ctx, &conn );
244 if (!ERR_DNS_IS_OK(err)) goto error;
245
246 if (!(upcaserealm = talloc_strdup(mem_ctx, target_realm))) {
247 err = ERROR_DNS_NO_MEMORY;
248 goto error;
249 }
250
251 strupr(upcaserealm);
252
253 if (!(targetname = talloc_asprintf(mem_ctx, "dns/%s@%s",
254 servername, upcaserealm))) {
255 err = ERROR_DNS_NO_MEMORY;
256 goto error;
257 }
258
259 input_name.value = targetname;
260 input_name.length = strlen(targetname);
261
262 major = gss_import_name( &minor, &input_name,
263 &nt_host_oid_desc, &targ_name );
264
265 if (major) {
266 err = ERROR_DNS_GSS_ERROR;
267 goto error;
268 }
269
270 err = dns_negotiate_gss_ctx_int(mem_ctx, conn, keyname,
271 targ_name, gss_ctx, srv_type );
272
273 gss_release_name( &minor, &targ_name );
274
275 error:
276 TALLOC_FREE(mem_ctx);
277
278 return err;
279 }
280
dns_sign_update(struct dns_update_request * req,gss_ctx_id_t gss_ctx,const char * keyname,const char * algorithmname,time_t time_signed,uint16_t fudge)281 DNS_ERROR dns_sign_update(struct dns_update_request *req,
282 gss_ctx_id_t gss_ctx,
283 const char *keyname,
284 const char *algorithmname,
285 time_t time_signed, uint16_t fudge)
286 {
287 struct dns_buffer *buf;
288 DNS_ERROR err;
289 struct dns_domain_name *key, *algorithm;
290 struct gss_buffer_desc_struct msg, mic;
291 OM_uint32 major, minor;
292 struct dns_rrec *rec;
293
294 err = dns_marshall_update_request(req, req, &buf);
295 if (!ERR_DNS_IS_OK(err)) return err;
296
297 err = dns_domain_name_from_string(buf, keyname, &key);
298 if (!ERR_DNS_IS_OK(err)) goto error;
299
300 err = dns_domain_name_from_string(buf, algorithmname, &algorithm);
301 if (!ERR_DNS_IS_OK(err)) goto error;
302
303 dns_marshall_domain_name(buf, key);
304 dns_marshall_uint16(buf, DNS_CLASS_ANY);
305 dns_marshall_uint32(buf, 0); /* TTL */
306 dns_marshall_domain_name(buf, algorithm);
307 dns_marshall_uint16(buf, 0); /* Time prefix for 48-bit time_t */
308 dns_marshall_uint32(buf, time_signed);
309 dns_marshall_uint16(buf, fudge);
310 dns_marshall_uint16(buf, 0); /* error */
311 dns_marshall_uint16(buf, 0); /* other len */
312
313 err = buf->error;
314 if (!ERR_DNS_IS_OK(buf->error)) goto error;
315
316 msg.value = (void *)buf->data;
317 msg.length = buf->offset;
318
319 major = gss_get_mic(&minor, gss_ctx, 0, &msg, &mic);
320 if (major != 0) {
321 err = ERROR_DNS_GSS_ERROR;
322 goto error;
323 }
324
325 if (mic.length > 0xffff) {
326 gss_release_buffer(&minor, &mic);
327 err = ERROR_DNS_GSS_ERROR;
328 goto error;
329 }
330
331 err = dns_create_tsig_record(buf, keyname, algorithmname, time_signed,
332 fudge, mic.length, (uint8_t *)mic.value,
333 req->id, 0, &rec);
334 gss_release_buffer(&minor, &mic);
335 if (!ERR_DNS_IS_OK(err)) goto error;
336
337 err = dns_add_rrec(req, rec, &req->num_additionals, &req->additionals);
338
339 error:
340 TALLOC_FREE(buf);
341 return err;
342 }
343
344 #endif /* HAVE_GSSAPI */
345