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