xref: /openbsd/usr.sbin/ypserv/ypxfr/ypxfr.c (revision 4bdff4be)
1 /*	$OpenBSD: ypxfr.c,v 1.39 2015/02/09 23:00:15 deraadt Exp $ */
2 
3 /*
4  * Copyright (c) 1994 Mats O Jansson <moj@stacken.kth.se>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
17  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/socket.h>
32 
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <string.h>
41 #include <netdb.h>
42 
43 #include <rpc/rpc.h>
44 #include <rpc/xdr.h>
45 #include <rpcsvc/yp.h>
46 #include <rpcsvc/ypclnt.h>
47 
48 #include "yplib_host.h"
49 #include "yplog.h"
50 #include "ypdb.h"
51 #include "ypdef.h"
52 
53 DBM	*db;
54 
55 static int
56 ypxfr_foreach(u_long status, char *keystr, int keylen, char *valstr, int vallen,
57     void *data)
58 {
59 	datum key, val;
60 
61 	if (status == YP_NOMORE)
62 		return(0);
63 
64 	keystr[keylen] = '\0';
65 	valstr[vallen] = '\0';
66 
67 	key.dptr = keystr;
68 	key.dsize = strlen(keystr);
69 
70 	val.dptr = valstr;
71 	val.dsize = strlen(valstr);
72 
73 	ypdb_store(db, key, val, YPDB_INSERT);
74 	return 0;
75 }
76 
77 static int
78 get_local_ordernum(char *domain, char *map, u_int32_t *lordernum)
79 {
80 	char map_path[PATH_MAX], order[MAX_LAST_LEN+1];
81 	char order_key[] = YP_LAST_KEY;
82 	struct stat finfo;
83 	datum k, v;
84 	int status;
85 	DBM *db;
86 
87 	/* This routine returns YPPUSH_SUCC or YPPUSH_NODOM */
88 
89 	status = YPPUSH_SUCC;
90 
91 	snprintf(map_path, sizeof map_path, "%s/%s", YP_DB_PATH, domain);
92 	if (!((stat(map_path, &finfo) == 0) && S_ISDIR(finfo.st_mode))) {
93 		fprintf(stderr, "ypxfr: domain %s not found locally\n",
94 		    domain);
95 		status = YPPUSH_NODOM;
96 		goto bail;
97 	}
98 
99 	snprintf(map_path, sizeof map_path, "%s/%s/%s%s",
100 	    YP_DB_PATH, domain, map, YPDB_SUFFIX);
101 	if (!(stat(map_path, &finfo) == 0)) {
102 		status = YPPUSH_NOMAP;
103 		goto bail;
104 	}
105 
106 	snprintf(map_path, sizeof map_path, "%s/%s/%s",
107 	    YP_DB_PATH, domain, map);
108 	db = ypdb_open(map_path, O_RDONLY, 0444);
109 	if (db == NULL) {
110 		status = YPPUSH_DBM;
111 		goto bail;
112 	}
113 
114 	k.dptr = (char *)&order_key;
115 	k.dsize = YP_LAST_LEN;
116 
117 	v = ypdb_fetch(db, k);
118 
119 	if (v.dptr == NULL) {
120 		*lordernum = 0;
121 	} else {
122 		strlcpy(order, v.dptr, sizeof order);
123 		*lordernum = (u_int32_t)atol(order);
124 	}
125 
126 	ypdb_close(db);
127 bail:
128 	if (status == YPPUSH_NOMAP || status == YPPUSH_DBM) {
129 		*lordernum = 0;
130 		status = YPPUSH_SUCC;
131 	}
132 	return (status);
133 
134 }
135 
136 static int
137 get_remote_ordernum(CLIENT *client, char *domain, char *map,
138     u_int32_t lordernum, u_int32_t *rordernum)
139 {
140 	int status;
141 
142 	status = yp_order_host(client, domain, map, rordernum);
143 
144 	if (status == 0) {
145 		if (*rordernum <= lordernum)
146 			status = YPPUSH_AGE;
147 		else
148 			status = YPPUSH_SUCC;
149 	}
150 	return status;
151 }
152 
153 static int
154 get_map(CLIENT *client, char *domain, char *map,
155     struct ypall_callback *incallback)
156 {
157 	int	status;
158 
159 	status = yp_all_host(client, domain, map, incallback);
160 	if (status == 0 || status == YPERR_NOMORE)
161 		status = YPPUSH_SUCC;
162 	else
163 		status = YPPUSH_YPERR;
164 	return (status);
165 }
166 
167 static DBM *
168 create_db(char *domain, char *map, char *temp_map)
169 {
170 	return ypdb_open_suf(temp_map, O_RDWR, 0444);
171 }
172 
173 static int
174 install_db(char *domain, char *map, char *temp_map)
175 {
176 	char	db_name[PATH_MAX];
177 
178 	snprintf(db_name, sizeof db_name, "%s/%s/%s%s",
179 	    YP_DB_PATH, domain, map, YPDB_SUFFIX);
180 	rename(temp_map, db_name);
181 	return YPPUSH_SUCC;
182 }
183 
184 static int
185 add_order(DBM *db, u_int32_t ordernum)
186 {
187 	char	datestr[11];
188 	datum	key, val;
189 	char	keystr[] = YP_LAST_KEY;
190 	int	status;
191 
192 	snprintf(datestr, sizeof datestr, "%010u", ordernum);
193 
194 	key.dptr = keystr;
195 	key.dsize = strlen(keystr);
196 
197 	val.dptr = datestr;
198 	val.dsize = strlen(datestr);
199 
200 	status = ypdb_store(db, key, val, YPDB_INSERT);
201 	if (status >= 0)
202 		status = YPPUSH_SUCC;
203 	else
204 		status = YPPUSH_DBM;
205 	return (status);
206 }
207 
208 static int
209 add_master(CLIENT *client, char *domain, char *map, DBM *db)
210 {
211 	char	keystr[] = YP_MASTER_KEY, *master = NULL;
212 	datum	key, val;
213 	int	status;
214 
215 	/* Get MASTER */
216 	status = yp_master_host(client, domain, map, &master);
217 
218 	if (master != NULL) {
219 		key.dptr = keystr;
220 		key.dsize = strlen(keystr);
221 
222 		val.dptr = master;
223 		val.dsize = strlen(master);
224 
225 		status = ypdb_store(db, key, val, YPDB_INSERT);
226 		if (status >= 0)
227 			status = YPPUSH_SUCC;
228 		else
229 			status = YPPUSH_DBM;
230 	}
231 	return (status);
232 }
233 
234 static int
235 add_interdomain(CLIENT *client, char *domain, char *map, DBM *db)
236 {
237 	char	keystr[] = YP_INTERDOMAIN_KEY, *value;
238 	int	vallen, status;
239 	datum	k, v;
240 
241 	/* Get INTERDOMAIN */
242 
243 	k.dptr = keystr;
244 	k.dsize = strlen(keystr);
245 
246 	status = yp_match_host(client, domain, map,
247 	    k.dptr, k.dsize, &value, &vallen);
248 	if (status == 0 && value) {
249 		v.dptr = value;
250 		v.dsize = vallen;
251 
252 		if (v.dptr != NULL) {
253 			status = ypdb_store(db, k, v, YPDB_INSERT);
254 			if (status >= 0)
255 				status = YPPUSH_SUCC;
256 			else
257 				status = YPPUSH_DBM;
258 		}
259 	}
260 	return 1;
261 }
262 
263 static int
264 add_secure(CLIENT *client, char *domain, char *map, DBM *db)
265 {
266 	char	keystr[] = YP_SECURE_KEY, *value;
267 	int	vallen, status;
268 	datum	k, v;
269 
270 	/* Get SECURE */
271 
272 	k.dptr = keystr;
273 	k.dsize = strlen(keystr);
274 
275 	status = yp_match_host(client, domain, map,
276 	    k.dptr, k.dsize, &value, &vallen);
277 	if (status == 0 && value) {
278 		v.dptr = value;
279 		v.dsize = vallen;
280 
281 		if (v.dptr != NULL) {
282 			status = ypdb_store(db, k, v, YPDB_INSERT);
283 			if (status >= 0)
284 				status = YPPUSH_SUCC;
285 			else
286 				status = YPPUSH_DBM;
287 		}
288 	}
289 	return status;
290 }
291 
292 static int
293 send_clear(CLIENT *client)
294 {
295 	struct	timeval tv;
296 	int	status, r;
297 
298 	status = YPPUSH_SUCC;
299 
300 	tv.tv_sec = 10;
301 	tv.tv_usec = 0;
302 
303 	/* Send CLEAR */
304 	r = clnt_call(client, YPPROC_CLEAR, xdr_void, 0, xdr_void, 0, tv);
305 	if (r != RPC_SUCCESS)
306 		clnt_perror(client, "yp_clear: clnt_call");
307 	return status;
308 
309 }
310 
311 static int
312 send_reply(CLIENT *client, u_long status, u_long tid)
313 {
314 	struct	ypresp_xfr resp;
315 	struct	timeval tv;
316 	int	r;
317 
318 	tv.tv_sec = 10;
319 	tv.tv_usec = 0;
320 
321 	resp.transid = tid;
322 	resp.xfrstat = status;
323 
324 	/* Send CLEAR */
325 	r = clnt_call(client, 1, xdr_ypresp_xfr, &resp, xdr_void, 0, tv);
326 	if (r != RPC_SUCCESS)
327 		clnt_perror(client, "yppushresp_xdr: clnt_call");
328 	return status;
329 
330 }
331 
332 static void
333 usage(void)
334 {
335 	fprintf(stderr,
336 	    "usage: ypxfr [-cf] [-C tid prog ipadd port] [-d domain] "
337 	    "[-h host] [-s domain]\n"
338 	    "             mapname\n");
339 	exit(1);
340 }
341 
342 int
343 main(int argc, char *argv[])
344 {
345 	int	 cflag = 0, fflag = 0, Cflag = 0;
346 	char	 *domain, *host = NULL, *srcdomain = NULL;
347 	char	 *tid = NULL, *prog = NULL, *ipadd = NULL;
348 	char	 *port = NULL, *map = NULL;
349 	int	 status, xfr_status, ch, srvport;
350 	u_int32_t ordernum, new_ordernum;
351 	struct	 ypall_callback callback;
352 	CLIENT   *client = NULL;
353 	extern	 char *optarg;
354 
355 	yp_get_default_domain(&domain);
356 
357 	while ((ch = getopt(argc, argv, "cd:fh:s:C:")) != -1)
358 		switch (ch) {
359 		case 'c':
360 			cflag = 1;
361 			break;
362 		case 'd':
363 			if (strchr(optarg, '/')) /* Ha ha, we are not listening */
364 				break;
365 			domain = optarg;
366 			break;
367 		case 'f':
368 			fflag = 1;
369 			break;
370 		case 'h':
371 			host = optarg;
372 			break;
373 		case 's':
374 			if (strchr(optarg, '/')) /* Ha ha, we are not listening */
375 				break;
376 			srcdomain = optarg;
377 			break;
378 		case 'C':
379 			if (optind + 3 >= argc)
380 				usage();
381 			Cflag = 1;
382 			tid = optarg;
383 			prog = argv[optind++];
384 			ipadd = argv[optind++];
385 			port = argv[optind++];
386 			break;
387 		default:
388 			usage();
389 			break;
390 		}
391 
392 	status = YPPUSH_SUCC;
393 
394 	if (optind + 1 != argc)
395 		usage();
396 
397 	map = argv[optind];
398 
399 	if (status > 0) {
400 		ypopenlog();
401 
402 		yplog("ypxfr: Arguments:");
403 		yplog("YP clear to local: %s", (cflag) ? "no" : "yes");
404 		yplog("   Force transfer: %s", (fflag) ? "yes" : "no");
405 		yplog("           domain: %s", domain);
406 		yplog("             host: %s", host);
407 		yplog("    source domain: %s", srcdomain);
408 		yplog("          transid: %s", tid);
409 		yplog("             prog: %s", prog);
410 		yplog("             port: %s", port);
411 		yplog("            ipadd: %s", ipadd);
412 		yplog("              map: %s", map);
413 
414 		if (fflag != 0) {
415 			ordernum = 0;
416 		} else {
417 			status = get_local_ordernum(domain, map, &ordernum);
418 		}
419 	}
420 
421 	if (status > 0) {
422 		yplog("Get Master");
423 
424 		if (host == NULL) {
425 			if (srcdomain == NULL) {
426 				status = yp_master(domain, map, &host);
427 			} else {
428 				status = yp_master(srcdomain, map, &host);
429 			}
430 			if (status == 0) {
431 				status = YPPUSH_SUCC;
432 			} else {
433 				status = -status;
434 			}
435 		}
436 	}
437 
438 	/* XXX this is raceable if portmap has holes! */
439 	if (status > 0) {
440 		yplog("Check for reserved port on host: %s", host);
441 
442 		srvport = getrpcport(host, YPPROG, YPVERS, IPPROTO_TCP);
443 		if (srvport >= IPPORT_RESERVED)
444 			status = YPPUSH_REFUSED;
445 	}
446 
447 	if (status > 0) {
448 		yplog("Connect host: %s", host);
449 
450 		client = yp_bind_host(host, YPPROG, YPVERS, 0, 1);
451 
452 		status = get_remote_ordernum(client, domain, map,
453 		    ordernum, &new_ordernum);
454 	}
455 
456 	if (status == YPPUSH_SUCC) {
457 		char	tmpmapname[PATH_MAX];
458 		int	fd;
459 
460 		/* Create temporary db */
461 		snprintf(tmpmapname, sizeof tmpmapname,
462 		    "%s/%s/ypdbXXXXXXXXXX", YP_DB_PATH, domain);
463 		fd = mkstemp(tmpmapname);
464 		if (fd == -1)
465 			status = YPPUSH_DBM;
466 		else
467 			close(fd);
468 
469 		if (status > 0) {
470 			db = create_db(domain, map, tmpmapname);
471 			if (db == NULL)
472 				status = YPPUSH_DBM;
473 		}
474 
475 		/* Add ORDER */
476 		if (status > 0)
477 			status = add_order(db, new_ordernum);
478 
479 		/* Add MASTER */
480 		if (status > 0)
481 			status = add_master(client, domain, map, db);
482 
483 		/* Add INTERDOMAIN */
484 		if (status > 0)
485 			status = add_interdomain(client, domain, map, db);
486 
487 		/* Add SECURE */
488 		if (status > 0)
489 			status = add_secure(client, domain, map, db);
490 
491 		if (status > 0) {
492 			callback.foreach = ypxfr_foreach;
493 			status = get_map(client, domain, map, &callback);
494 		}
495 
496 		/* Close db */
497 		if (db != NULL)
498 			ypdb_close(db);
499 
500 		/* Rename db */
501 		if (status > 0) {
502 			status = install_db(domain, map, tmpmapname);
503 		} else {
504 			unlink(tmpmapname);
505 			status = YPPUSH_SUCC;
506 		}
507 	}
508 
509 	xfr_status = status;
510 
511 	if (client != NULL)
512 		clnt_destroy(client);
513 
514 	/* YP_CLEAR */
515 
516 	if (!cflag) {
517 		client = yp_bind_local(YPPROG, YPVERS);
518 		status = send_clear(client);
519 		clnt_destroy(client);
520 	}
521 
522 	if (Cflag > 0) {
523 		/* Send Response */
524 		client = yp_bind_host(ipadd, atoi(prog), 1, atoi(port), 0);
525 		status = send_reply(client, xfr_status, atoi(tid));
526 		clnt_destroy(client);
527 	}
528 	return (0);
529 }
530