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