1 /* 2 * Copyright (c) 1992, Mark D. Baushke 3 * 4 * You may distribute under the terms of the GNU General Public License as 5 * specified in the README file that comes with the CVS source distribution. 6 * 7 * Name of Root 8 * 9 * Determine the path to the CVSROOT and set "Root" accordingly. 10 */ 11 12 #include "cvs.h" 13 #include "getline.h" 14 15 /* Printable names for things in the CVSroot_method enum variable. 16 Watch out if the enum is changed in cvs.h! */ 17 18 char *method_names[] = { 19 "local", "server (ssh)", "pserver", "kserver", "gserver", "ext", "fork" 20 }; 21 22 #ifndef DEBUG 23 24 char * 25 Name_Root (dir, update_dir) 26 char *dir; 27 char *update_dir; 28 { 29 FILE *fpin; 30 char *ret, *xupdate_dir; 31 char *root = NULL; 32 size_t root_allocated = 0; 33 char *tmp; 34 char *cvsadm; 35 char *cp; 36 37 if (update_dir && *update_dir) 38 xupdate_dir = update_dir; 39 else 40 xupdate_dir = "."; 41 42 if (dir != NULL) 43 { 44 cvsadm = xmalloc (strlen (dir) + sizeof (CVSADM) + 10); 45 (void) sprintf (cvsadm, "%s/%s", dir, CVSADM); 46 tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); 47 (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); 48 } 49 else 50 { 51 cvsadm = xstrdup (CVSADM); 52 tmp = xstrdup (CVSADM_ROOT); 53 } 54 55 /* 56 * Do not bother looking for a readable file if there is no cvsadm 57 * directory present. 58 * 59 * It is possible that not all repositories will have a CVS/Root 60 * file. This is ok, but the user will need to specify -d 61 * /path/name or have the environment variable CVSROOT set in 62 * order to continue. */ 63 if ((!isdir (cvsadm)) || (!isreadable (tmp))) 64 { 65 ret = NULL; 66 goto out; 67 } 68 69 /* 70 * The assumption here is that the CVS Root is always contained in the 71 * first line of the "Root" file. 72 */ 73 fpin = open_file (tmp, "r"); 74 75 if (getline (&root, &root_allocated, fpin) < 0) 76 { 77 /* FIXME: should be checking for end of file separately; errno 78 is not set in that case. */ 79 error (0, 0, "in directory %s:", xupdate_dir); 80 error (0, errno, "cannot read %s", CVSADM_ROOT); 81 error (0, 0, "please correct this problem"); 82 ret = NULL; 83 goto out; 84 } 85 (void) fclose (fpin); 86 if ((cp = strrchr (root, '\n')) != NULL) 87 *cp = '\0'; /* strip the newline */ 88 89 /* 90 * root now contains a candidate for CVSroot. It must be an 91 * absolute pathname or specify a remote server. 92 */ 93 94 if ( 95 #ifdef CLIENT_SUPPORT 96 (strchr (root, ':') == NULL) && 97 #endif 98 ! isabsolute (root)) 99 { 100 error (0, 0, "in directory %s:", xupdate_dir); 101 error (0, 0, 102 "ignoring %s because it does not contain an absolute pathname.", 103 CVSADM_ROOT); 104 ret = NULL; 105 goto out; 106 } 107 108 #ifdef CLIENT_SUPPORT 109 if ((strchr (root, ':') == NULL) && !isdir (root)) 110 #else /* ! CLIENT_SUPPORT */ 111 if (!isdir (root)) 112 #endif /* CLIENT_SUPPORT */ 113 { 114 error (0, 0, "in directory %s:", xupdate_dir); 115 error (0, 0, 116 "ignoring %s because it specifies a non-existent repository %s", 117 CVSADM_ROOT, root); 118 ret = NULL; 119 goto out; 120 } 121 122 /* allocate space to return and fill it in */ 123 strip_trailing_slashes (root); 124 ret = xstrdup (root); 125 out: 126 free (cvsadm); 127 free (tmp); 128 if (root != NULL) 129 free (root); 130 return (ret); 131 } 132 133 /* 134 * Write the CVS/Root file so that the environment variable CVSROOT 135 * and/or the -d option to cvs will be validated or not necessary for 136 * future work. 137 */ 138 void 139 Create_Root (dir, rootdir) 140 char *dir; 141 char *rootdir; 142 { 143 FILE *fout; 144 char *tmp; 145 146 if (noexec) 147 return; 148 149 /* record the current cvs root */ 150 151 if (rootdir != NULL) 152 { 153 if (dir != NULL) 154 { 155 tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); 156 (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); 157 } 158 else 159 tmp = xstrdup (CVSADM_ROOT); 160 161 fout = open_file (tmp, "w+"); 162 if (fprintf (fout, "%s\n", rootdir) < 0) 163 error (1, errno, "write to %s failed", tmp); 164 if (fclose (fout) == EOF) 165 error (1, errno, "cannot close %s", tmp); 166 free (tmp); 167 } 168 } 169 170 #endif /* ! DEBUG */ 171 172 173 /* The root_allow_* stuff maintains a list of legal CVSROOT 174 directories. Then we can check against them when a remote user 175 hands us a CVSROOT directory. */ 176 177 static int root_allow_count; 178 static char **root_allow_vector; 179 static int root_allow_size; 180 181 void 182 root_allow_add (arg) 183 char *arg; 184 { 185 char *p; 186 187 if (root_allow_size <= root_allow_count) 188 { 189 if (root_allow_size == 0) 190 { 191 root_allow_size = 1; 192 root_allow_vector = 193 (char **) malloc (root_allow_size * sizeof (char *)); 194 } 195 else 196 { 197 root_allow_size *= 2; 198 root_allow_vector = 199 (char **) realloc (root_allow_vector, 200 root_allow_size * sizeof (char *)); 201 } 202 203 if (root_allow_vector == NULL) 204 { 205 no_memory: 206 /* Strictly speaking, we're not supposed to output anything 207 now. But we're about to exit(), give it a try. */ 208 printf ("E Fatal server error, aborting.\n\ 209 error ENOMEM Virtual memory exhausted.\n"); 210 211 /* I'm doing this manually rather than via error_exit () 212 because I'm not sure whether we want to call server_cleanup. 213 Needs more investigation.... */ 214 215 #ifdef SYSTEM_CLEANUP 216 /* Hook for OS-specific behavior, for example socket 217 subsystems on NT and OS2 or dealing with windows 218 and arguments on Mac. */ 219 SYSTEM_CLEANUP (); 220 #endif 221 222 exit (EXIT_FAILURE); 223 } 224 } 225 p = malloc (strlen (arg) + 1); 226 if (p == NULL) 227 goto no_memory; 228 strcpy (p, arg); 229 root_allow_vector[root_allow_count++] = p; 230 } 231 232 void 233 root_allow_free () 234 { 235 if (root_allow_vector != NULL) 236 free_names (&root_allow_count, root_allow_vector); 237 root_allow_size = 0; 238 } 239 240 int 241 root_allow_ok (arg) 242 char *arg; 243 { 244 int i; 245 246 if (root_allow_count == 0) 247 { 248 /* Probably someone upgraded from CVS before 1.9.10 to 1.9.10 249 or later without reading the documentation about 250 --allow-root. Printing an error here doesn't disclose any 251 particularly useful information to an attacker because a 252 CVS server configured in this way won't let *anyone* in. */ 253 254 /* Note that we are called from a context where we can spit 255 back "error" rather than waiting for the next request which 256 expects responses. */ 257 printf ("\ 258 error 0 Server configuration missing --allow-root in inetd.conf\n"); 259 error_exit (); 260 } 261 262 for (i = 0; i < root_allow_count; ++i) 263 if (strcmp (root_allow_vector[i], arg) == 0) 264 return 1; 265 return 0; 266 } 267 268 /* This global variable holds the global -d option. It is NULL if -d 269 was not used, which means that we must get the CVSroot information 270 from the CVSROOT environment variable or from a CVS/Root file. */ 271 272 char *CVSroot_cmdline; 273 274 /* Parse a CVSROOT variable into its constituent parts -- method, 275 * username, hostname, directory. The prototypical CVSROOT variable 276 * looks like: 277 * 278 * :method:user@host:path 279 * 280 * Some methods may omit fields; local, for example, doesn't need user 281 * and host. 282 * 283 * Returns zero on success, non-zero on failure. */ 284 285 char *CVSroot_original = NULL; /* the CVSroot that was passed in */ 286 int client_active; /* nonzero if we are doing remote access */ 287 CVSmethod CVSroot_method; /* one of the enum values defined in cvs.h */ 288 char *CVSroot_username; /* the username or NULL if method == local */ 289 char *CVSroot_hostname; /* the hostname or NULL if method == local */ 290 char *CVSroot_directory; /* the directory name */ 291 292 int 293 parse_cvsroot (CVSroot) 294 char *CVSroot; 295 { 296 static int cvsroot_parsed = 0; 297 char *cvsroot_copy, *cvsroot_save, *p; 298 int check_hostname; 299 300 /* Don't go through the trouble twice. */ 301 if (cvsroot_parsed) 302 { 303 error (0, 0, "WARNING (parse_cvsroot): someone called me twice!\n"); 304 return 0; 305 } 306 307 if (CVSroot_original != NULL) 308 free (CVSroot_original); 309 if (CVSroot_directory != NULL) 310 free (CVSroot_directory); 311 if (CVSroot_username != NULL) 312 free (CVSroot_username); 313 if (CVSroot_hostname != NULL) 314 free (CVSroot_hostname); 315 316 CVSroot_original = xstrdup (CVSroot); 317 cvsroot_save = cvsroot_copy = xstrdup (CVSroot); 318 319 if (*cvsroot_copy == ':') 320 { 321 char *method = ++cvsroot_copy; 322 323 /* Access method specified, as in 324 * "cvs -d :pserver:user@host:/path", 325 * "cvs -d :local:e:\path", 326 * "cvs -d :kserver:user@host:/path", or 327 * "cvs -d :fork:/path". 328 * We need to get past that part of CVSroot before parsing the 329 * rest of it. 330 */ 331 332 if (! (p = strchr (method, ':'))) 333 { 334 error (0, 0, "bad CVSroot: %s", CVSroot); 335 free (cvsroot_save); 336 return 1; 337 } 338 *p = '\0'; 339 cvsroot_copy = ++p; 340 341 /* Now we have an access method -- see if it's valid. */ 342 343 if (strcmp (method, "local") == 0) 344 CVSroot_method = local_method; 345 else if (strcmp (method, "pserver") == 0) 346 CVSroot_method = pserver_method; 347 else if (strcmp (method, "kserver") == 0) 348 CVSroot_method = kserver_method; 349 else if (strcmp (method, "gserver") == 0) 350 CVSroot_method = gserver_method; 351 else if (strcmp (method, "server") == 0) 352 CVSroot_method = server_method; 353 else if (strcmp (method, "ext") == 0) 354 CVSroot_method = ext_method; 355 else if (strcmp (method, "fork") == 0) 356 CVSroot_method = fork_method; 357 else 358 { 359 error (0, 0, "unknown method in CVSroot: %s", CVSroot); 360 free (cvsroot_save); 361 return 1; 362 } 363 } 364 else 365 { 366 /* If the method isn't specified, assume 367 SERVER_METHOD/EXT_METHOD if the string contains a colon or 368 LOCAL_METHOD otherwise. */ 369 370 CVSroot_method = ((strchr (cvsroot_copy, ':')) 371 #ifdef RSH_NOT_TRANSPARENT 372 ? server_method 373 #else 374 ? ext_method 375 #endif 376 : local_method); 377 } 378 379 client_active = (CVSroot_method != local_method); 380 381 /* Check for username/hostname if we're not LOCAL_METHOD. */ 382 383 CVSroot_username = NULL; 384 CVSroot_hostname = NULL; 385 386 if ((CVSroot_method != local_method) 387 && (CVSroot_method != fork_method)) 388 { 389 /* Check to see if there is a username in the string. */ 390 391 if ((p = strchr (cvsroot_copy, '@')) != NULL) 392 { 393 *p = '\0'; 394 CVSroot_username = xstrdup (cvsroot_copy); 395 cvsroot_copy = ++p; 396 if (*CVSroot_username == '\0') 397 CVSroot_username = NULL; 398 } 399 400 if (*cvsroot_copy == '[') 401 { 402 p = strchr(cvsroot_copy, ']'); 403 if (p != NULL) 404 { 405 *p = '\0'; 406 CVSroot_hostname = xstrdup (cvsroot_copy+1); 407 *p++ = ']'; 408 if (*p == ':') 409 cvsroot_copy = p+1; 410 } 411 } 412 else if ((p = strchr (cvsroot_copy, ':')) != NULL) 413 { 414 *p = '\0'; 415 CVSroot_hostname = xstrdup (cvsroot_copy); 416 cvsroot_copy = ++p; 417 418 if (*CVSroot_hostname == '\0') 419 CVSroot_hostname = NULL; 420 } 421 } 422 423 CVSroot_directory = xstrdup(cvsroot_copy); 424 free (cvsroot_save); 425 426 #if ! defined (CLIENT_SUPPORT) && ! defined (DEBUG) 427 if (CVSroot_method != local_method) 428 { 429 error (0, 0, "Your CVSROOT is set for a remote access method"); 430 error (0, 0, "but your CVS executable doesn't support it"); 431 error (0, 0, "(%s)", CVSroot); 432 return 1; 433 } 434 #endif 435 436 /* Do various sanity checks. */ 437 438 if (CVSroot_username && ! CVSroot_hostname) 439 { 440 error (0, 0, "missing hostname in CVSROOT: %s", CVSroot); 441 return 1; 442 } 443 444 check_hostname = 0; 445 switch (CVSroot_method) 446 { 447 case local_method: 448 if (CVSroot_username || CVSroot_hostname) 449 { 450 error (0, 0, "can't specify hostname and username in CVSROOT"); 451 error (0, 0, "when using local access method"); 452 error (0, 0, "(%s)", CVSroot); 453 return 1; 454 } 455 /* cvs.texinfo has always told people that CVSROOT must be an 456 absolute pathname. Furthermore, attempts to use a relative 457 pathname produced various errors (I couldn't get it to work), 458 so there would seem to be little risk in making this a fatal 459 error. */ 460 if (!isabsolute (CVSroot_directory)) 461 error (1, 0, "CVSROOT %s must be an absolute pathname", 462 CVSroot_directory); 463 break; 464 case fork_method: 465 /* We want :fork: to behave the same as other remote access 466 methods. Therefore, don't check to see that the repository 467 name is absolute -- let the server do it. */ 468 if (CVSroot_username || CVSroot_hostname) 469 { 470 error (0, 0, "can't specify hostname and username in CVSROOT"); 471 error (0, 0, "when using fork access method"); 472 error (0, 0, "(%s)", CVSroot); 473 return 1; 474 } 475 break; 476 case kserver_method: 477 #ifndef HAVE_KERBEROS 478 error (0, 0, "Your CVSROOT is set for a kerberos access method"); 479 error (0, 0, "but your CVS executable doesn't support it"); 480 error (0, 0, "(%s)", CVSroot); 481 return 1; 482 #else 483 check_hostname = 1; 484 break; 485 #endif 486 case gserver_method: 487 #ifndef HAVE_GSSAPI 488 error (0, 0, "Your CVSROOT is set for a GSSAPI access method"); 489 error (0, 0, "but your CVS executable doesn't support it"); 490 error (0, 0, "(%s)", CVSroot); 491 return 1; 492 #else 493 check_hostname = 1; 494 break; 495 #endif 496 case server_method: 497 case ext_method: 498 case pserver_method: 499 check_hostname = 1; 500 break; 501 } 502 503 if (check_hostname) 504 { 505 if (! CVSroot_hostname) 506 { 507 error (0, 0, "didn't specify hostname in CVSROOT: %s", CVSroot); 508 return 1; 509 } 510 } 511 512 if (*CVSroot_directory == '\0') 513 { 514 error (0, 0, "missing directory in CVSROOT: %s", CVSroot); 515 return 1; 516 } 517 518 /* Hooray! We finally parsed it! */ 519 return 0; 520 } 521 522 523 /* Set up the global CVSroot* variables as if we're using the local 524 repository DIR. */ 525 526 void 527 set_local_cvsroot (dir) 528 char *dir; 529 { 530 if (CVSroot_original != NULL) 531 free (CVSroot_original); 532 CVSroot_original = xstrdup(dir); 533 CVSroot_method = local_method; 534 if (CVSroot_directory != NULL) 535 free (CVSroot_directory); 536 CVSroot_directory = xstrdup(dir); 537 if (CVSroot_username != NULL) 538 free (CVSroot_username); 539 CVSroot_username = NULL; 540 if (CVSroot_hostname != NULL) 541 free (CVSroot_hostname); 542 CVSroot_hostname = NULL; 543 client_active = 0; 544 } 545 546 547 #ifdef DEBUG 548 /* This is for testing the parsing function. Use 549 550 gcc -I. -I.. -I../lib -DDEBUG root.c -o root 551 552 to compile. */ 553 554 #include <stdio.h> 555 556 char *CVSroot; 557 char *program_name = "testing"; 558 char *command_name = "parse_cvsroot"; /* XXX is this used??? */ 559 560 /* Toy versions of various functions when debugging under unix. Yes, 561 these make various bad assumptions, but they're pretty easy to 562 debug when something goes wrong. */ 563 564 void 565 error_exit PROTO ((void)) 566 { 567 exit (1); 568 } 569 570 char * 571 xstrdup (str) 572 const char *str; 573 { 574 return strdup (str); 575 } 576 577 int 578 isabsolute (dir) 579 const char *dir; 580 { 581 return (dir && (*dir == '/')); 582 } 583 584 void 585 main (argc, argv) 586 int argc; 587 char *argv[]; 588 { 589 program_name = argv[0]; 590 591 if (argc != 2) 592 { 593 fprintf (stderr, "Usage: %s <CVSROOT>\n", program_name); 594 exit (2); 595 } 596 597 if (parse_cvsroot (argv[1])) 598 { 599 fprintf (stderr, "%s: Parsing failed.\n", program_name); 600 exit (1); 601 } 602 printf ("CVSroot: %s\n", argv[1]); 603 printf ("CVSroot_method: %s\n", method_names[CVSroot_method]); 604 printf ("CVSroot_username: %s\n", 605 CVSroot_username ? CVSroot_username : "NULL"); 606 printf ("CVSroot_hostname: %s\n", 607 CVSroot_hostname ? CVSroot_hostname : "NULL"); 608 printf ("CVSroot_directory: %s\n", CVSroot_directory); 609 610 exit (0); 611 /* NOTREACHED */ 612 } 613 #endif 614