root.c revision 54427
117721Speter/* 217721Speter * Copyright (c) 1992, Mark D. Baushke 317721Speter * 417721Speter * You may distribute under the terms of the GNU General Public License as 532785Speter * specified in the README file that comes with the CVS source distribution. 617721Speter * 717721Speter * Name of Root 817721Speter * 917721Speter * Determine the path to the CVSROOT and set "Root" accordingly. 1017721Speter */ 1117721Speter 1217721Speter#include "cvs.h" 1325839Speter#include "getline.h" 1417721Speter 1525839Speter/* Printable names for things in the CVSroot_method enum variable. 1625839Speter Watch out if the enum is changed in cvs.h! */ 1725839Speter 1825839Speterchar *method_names[] = { 1954427Speter "local", "server (rsh)", "pserver", "kserver", "gserver", "ext", "fork" 2025839Speter}; 2125839Speter 2225839Speter#ifndef DEBUG 2325839Speter 2417721Speterchar * 2532785SpeterName_Root (dir, update_dir) 2632785Speter char *dir; 2732785Speter char *update_dir; 2817721Speter{ 2917721Speter FILE *fpin; 3017721Speter char *ret, *xupdate_dir; 3125839Speter char *root = NULL; 3225839Speter size_t root_allocated = 0; 3325839Speter char *tmp; 3425839Speter char *cvsadm; 3517721Speter char *cp; 3617721Speter 3717721Speter if (update_dir && *update_dir) 3817721Speter xupdate_dir = update_dir; 3917721Speter else 4017721Speter xupdate_dir = "."; 4117721Speter 4217721Speter if (dir != NULL) 4317721Speter { 4425839Speter cvsadm = xmalloc (strlen (dir) + sizeof (CVSADM) + 10); 4517721Speter (void) sprintf (cvsadm, "%s/%s", dir, CVSADM); 4625839Speter tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); 4717721Speter (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); 4817721Speter } 4917721Speter else 5017721Speter { 5125839Speter cvsadm = xstrdup (CVSADM); 5225839Speter tmp = xstrdup (CVSADM_ROOT); 5317721Speter } 5417721Speter 5517721Speter /* 5617721Speter * Do not bother looking for a readable file if there is no cvsadm 5717721Speter * directory present. 5817721Speter * 5917721Speter * It is possible that not all repositories will have a CVS/Root 6017721Speter * file. This is ok, but the user will need to specify -d 6117721Speter * /path/name or have the environment variable CVSROOT set in 6225839Speter * order to continue. */ 6317721Speter if ((!isdir (cvsadm)) || (!isreadable (tmp))) 6417721Speter { 6525839Speter ret = NULL; 6625839Speter goto out; 6717721Speter } 6817721Speter 6917721Speter /* 7017721Speter * The assumption here is that the CVS Root is always contained in the 7117721Speter * first line of the "Root" file. 7217721Speter */ 7317721Speter fpin = open_file (tmp, "r"); 7417721Speter 7525839Speter if (getline (&root, &root_allocated, fpin) < 0) 7617721Speter { 7725839Speter /* FIXME: should be checking for end of file separately; errno 7825839Speter is not set in that case. */ 7917721Speter error (0, 0, "in directory %s:", xupdate_dir); 8017721Speter error (0, errno, "cannot read %s", CVSADM_ROOT); 8117721Speter error (0, 0, "please correct this problem"); 8225839Speter ret = NULL; 8325839Speter goto out; 8417721Speter } 8517721Speter (void) fclose (fpin); 8617721Speter if ((cp = strrchr (root, '\n')) != NULL) 8717721Speter *cp = '\0'; /* strip the newline */ 8817721Speter 8917721Speter /* 9017721Speter * root now contains a candidate for CVSroot. It must be an 9125839Speter * absolute pathname or specify a remote server. 9217721Speter */ 9317721Speter 9425839Speter if ( 9517721Speter#ifdef CLIENT_SUPPORT 9625839Speter (strchr (root, ':') == NULL) && 9725839Speter#endif 9825839Speter ! isabsolute (root)) 9917721Speter { 10017721Speter error (0, 0, "in directory %s:", xupdate_dir); 10117721Speter error (0, 0, 10217721Speter "ignoring %s because it does not contain an absolute pathname.", 10317721Speter CVSADM_ROOT); 10425839Speter ret = NULL; 10525839Speter goto out; 10617721Speter } 10717721Speter 10817721Speter#ifdef CLIENT_SUPPORT 10917721Speter if ((strchr (root, ':') == NULL) && !isdir (root)) 11017721Speter#else /* ! CLIENT_SUPPORT */ 11117721Speter if (!isdir (root)) 11217721Speter#endif /* CLIENT_SUPPORT */ 11317721Speter { 11417721Speter error (0, 0, "in directory %s:", xupdate_dir); 11517721Speter error (0, 0, 11617721Speter "ignoring %s because it specifies a non-existent repository %s", 11717721Speter CVSADM_ROOT, root); 11825839Speter ret = NULL; 11925839Speter goto out; 12017721Speter } 12117721Speter 12217721Speter /* allocate space to return and fill it in */ 12325839Speter strip_trailing_slashes (root); 12417721Speter ret = xstrdup (root); 12525839Speter out: 12625839Speter free (cvsadm); 12725839Speter free (tmp); 12825839Speter if (root != NULL) 12925839Speter free (root); 13017721Speter return (ret); 13117721Speter} 13217721Speter 13317721Speter/* 13417721Speter * Write the CVS/Root file so that the environment variable CVSROOT 13517721Speter * and/or the -d option to cvs will be validated or not necessary for 13617721Speter * future work. 13717721Speter */ 13817721Spetervoid 13917721SpeterCreate_Root (dir, rootdir) 14032785Speter char *dir; 14132785Speter char *rootdir; 14217721Speter{ 14317721Speter FILE *fout; 14425839Speter char *tmp; 14517721Speter 14617721Speter if (noexec) 14717721Speter return; 14817721Speter 14917721Speter /* record the current cvs root */ 15017721Speter 15117721Speter if (rootdir != NULL) 15217721Speter { 15317721Speter if (dir != NULL) 15425839Speter { 15525839Speter tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); 15617721Speter (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); 15725839Speter } 15817721Speter else 15925839Speter tmp = xstrdup (CVSADM_ROOT); 16025839Speter 16117721Speter fout = open_file (tmp, "w+"); 16217721Speter if (fprintf (fout, "%s\n", rootdir) < 0) 16317721Speter error (1, errno, "write to %s failed", tmp); 16417721Speter if (fclose (fout) == EOF) 16517721Speter error (1, errno, "cannot close %s", tmp); 16625839Speter free (tmp); 16717721Speter } 16817721Speter} 16925839Speter 17025839Speter#endif /* ! DEBUG */ 17125839Speter 17225839Speter 17326801Speter/* The root_allow_* stuff maintains a list of legal CVSROOT 17426801Speter directories. Then we can check against them when a remote user 17526801Speter hands us a CVSROOT directory. */ 17626801Speter 17726801Speterstatic unsigned int root_allow_count; 17826801Speterstatic char **root_allow_vector; 17926801Speterstatic unsigned int root_allow_size; 18026801Speter 18126801Spetervoid 18226801Speterroot_allow_add (arg) 18326801Speter char *arg; 18426801Speter{ 18526801Speter char *p; 18626801Speter 18726801Speter if (root_allow_size <= root_allow_count) 18826801Speter { 18926801Speter if (root_allow_size == 0) 19026801Speter { 19126801Speter root_allow_size = 1; 19226801Speter root_allow_vector = 19326801Speter (char **) malloc (root_allow_size * sizeof (char *)); 19426801Speter } 19526801Speter else 19626801Speter { 19726801Speter root_allow_size *= 2; 19826801Speter root_allow_vector = 19926801Speter (char **) realloc (root_allow_vector, 20026801Speter root_allow_size * sizeof (char *)); 20126801Speter } 20226801Speter 20326801Speter if (root_allow_vector == NULL) 20426801Speter { 20526801Speter no_memory: 20626801Speter /* Strictly speaking, we're not supposed to output anything 20726801Speter now. But we're about to exit(), give it a try. */ 20826801Speter printf ("E Fatal server error, aborting.\n\ 20926801Spetererror ENOMEM Virtual memory exhausted.\n"); 21026801Speter 21126801Speter /* I'm doing this manually rather than via error_exit () 21226801Speter because I'm not sure whether we want to call server_cleanup. 21326801Speter Needs more investigation.... */ 21426801Speter 21526801Speter#ifdef SYSTEM_CLEANUP 21626801Speter /* Hook for OS-specific behavior, for example socket 21726801Speter subsystems on NT and OS2 or dealing with windows 21826801Speter and arguments on Mac. */ 21926801Speter SYSTEM_CLEANUP (); 22026801Speter#endif 22126801Speter 22226801Speter exit (EXIT_FAILURE); 22326801Speter } 22426801Speter } 22526801Speter p = malloc (strlen (arg) + 1); 22626801Speter if (p == NULL) 22726801Speter goto no_memory; 22826801Speter strcpy (p, arg); 22926801Speter root_allow_vector[root_allow_count++] = p; 23026801Speter} 23126801Speter 23226801Spetervoid 23326801Speterroot_allow_free () 23426801Speter{ 23526801Speter if (root_allow_vector != NULL) 23626801Speter free (root_allow_vector); 23726801Speter root_allow_count = 0; 23826801Speter root_allow_size = 0; 23926801Speter} 24026801Speter 24126801Speterint 24226801Speterroot_allow_ok (arg) 24326801Speter char *arg; 24426801Speter{ 24526801Speter unsigned int i; 24632785Speter 24732785Speter if (root_allow_count == 0) 24832785Speter { 24932785Speter /* Probably someone upgraded from CVS before 1.9.10 to 1.9.10 25032785Speter or later without reading the documentation about 25132785Speter --allow-root. Printing an error here doesn't disclose any 25232785Speter particularly useful information to an attacker because a 25332785Speter CVS server configured in this way won't let *anyone* in. */ 25432785Speter 25532785Speter /* Note that we are called from a context where we can spit 25632785Speter back "error" rather than waiting for the next request which 25732785Speter expects responses. */ 25832785Speter printf ("\ 25932785Spetererror 0 Server configuration missing --allow-root in inetd.conf\n"); 26032785Speter error_exit (); 26132785Speter } 26232785Speter 26326801Speter for (i = 0; i < root_allow_count; ++i) 26426801Speter if (strcmp (root_allow_vector[i], arg) == 0) 26526801Speter return 1; 26626801Speter return 0; 26726801Speter} 26826801Speter 26954427Speter/* This global variable holds the global -d option. It is NULL if -d 27054427Speter was not used, which means that we must get the CVSroot information 27154427Speter from the CVSROOT environment variable or from a CVS/Root file. */ 27226801Speter 27354427Speterchar *CVSroot_cmdline; 27454427Speter 27525839Speter/* Parse a CVSROOT variable into its constituent parts -- method, 27625839Speter * username, hostname, directory. The prototypical CVSROOT variable 27725839Speter * looks like: 27825839Speter * 27925839Speter * :method:user@host:path 28025839Speter * 28125839Speter * Some methods may omit fields; local, for example, doesn't need user 28225839Speter * and host. 28325839Speter * 28425839Speter * Returns zero on success, non-zero on failure. */ 28525839Speter 28625839Speterchar *CVSroot_original = NULL; /* the CVSroot that was passed in */ 28725839Speterint client_active; /* nonzero if we are doing remote access */ 28825839SpeterCVSmethod CVSroot_method; /* one of the enum values defined in cvs.h */ 28925839Speterchar *CVSroot_username; /* the username or NULL if method == local */ 29025839Speterchar *CVSroot_hostname; /* the hostname or NULL if method == local */ 29125839Speterchar *CVSroot_directory; /* the directory name */ 29225839Speter 29325839Speterint 29425839Speterparse_cvsroot (CVSroot) 29525839Speter char *CVSroot; 29625839Speter{ 29725839Speter static int cvsroot_parsed = 0; 29825839Speter char *cvsroot_copy, *p; 29932785Speter int check_hostname; 30025839Speter 30125839Speter /* Don't go through the trouble twice. */ 30225839Speter if (cvsroot_parsed) 30325839Speter { 30425839Speter error (0, 0, "WARNING (parse_cvsroot): someone called me twice!\n"); 30525839Speter return 0; 30625839Speter } 30725839Speter 30825839Speter CVSroot_original = xstrdup (CVSroot); 30925839Speter cvsroot_copy = xstrdup (CVSroot); 31025839Speter 31125839Speter if ((*cvsroot_copy == ':')) 31225839Speter { 31325839Speter char *method = ++cvsroot_copy; 31425839Speter 31525839Speter /* Access method specified, as in 31625839Speter * "cvs -d :pserver:user@host:/path", 31754427Speter * "cvs -d :local:e:\path", 31854427Speter * "cvs -d :kserver:user@host:/path", or 31954427Speter * "cvs -d :fork:/path". 32025839Speter * We need to get past that part of CVSroot before parsing the 32125839Speter * rest of it. 32225839Speter */ 32325839Speter 32425839Speter if (! (p = strchr (method, ':'))) 32525839Speter { 32625839Speter error (0, 0, "bad CVSroot: %s", CVSroot); 32725839Speter return 1; 32825839Speter } 32925839Speter *p = '\0'; 33025839Speter cvsroot_copy = ++p; 33125839Speter 33225839Speter /* Now we have an access method -- see if it's valid. */ 33325839Speter 33425839Speter if (strcmp (method, "local") == 0) 33525839Speter CVSroot_method = local_method; 33625839Speter else if (strcmp (method, "pserver") == 0) 33725839Speter CVSroot_method = pserver_method; 33825839Speter else if (strcmp (method, "kserver") == 0) 33925839Speter CVSroot_method = kserver_method; 34032785Speter else if (strcmp (method, "gserver") == 0) 34132785Speter CVSroot_method = gserver_method; 34225839Speter else if (strcmp (method, "server") == 0) 34325839Speter CVSroot_method = server_method; 34425839Speter else if (strcmp (method, "ext") == 0) 34525839Speter CVSroot_method = ext_method; 34654427Speter else if (strcmp (method, "fork") == 0) 34754427Speter CVSroot_method = fork_method; 34825839Speter else 34925839Speter { 35025839Speter error (0, 0, "unknown method in CVSroot: %s", CVSroot); 35125839Speter return 1; 35225839Speter } 35325839Speter } 35425839Speter else 35525839Speter { 35625839Speter /* If the method isn't specified, assume 35725839Speter SERVER_METHOD/EXT_METHOD if the string contains a colon or 35825839Speter LOCAL_METHOD otherwise. */ 35925839Speter 36025839Speter CVSroot_method = ((strchr (cvsroot_copy, ':')) 36125839Speter#ifdef RSH_NOT_TRANSPARENT 36225839Speter ? server_method 36325839Speter#else 36425839Speter ? ext_method 36525839Speter#endif 36625839Speter : local_method); 36725839Speter } 36825839Speter 36925839Speter client_active = (CVSroot_method != local_method); 37025839Speter 37125839Speter /* Check for username/hostname if we're not LOCAL_METHOD. */ 37225839Speter 37325839Speter CVSroot_username = NULL; 37425839Speter CVSroot_hostname = NULL; 37525839Speter 37654427Speter if ((CVSroot_method != local_method) 37754427Speter && (CVSroot_method != fork_method)) 37825839Speter { 37925839Speter /* Check to see if there is a username in the string. */ 38025839Speter 38125839Speter if ((p = strchr (cvsroot_copy, '@'))) 38225839Speter { 38325839Speter CVSroot_username = cvsroot_copy; 38425839Speter *p = '\0'; 38525839Speter cvsroot_copy = ++p; 38625839Speter if (*CVSroot_username == '\0') 38725839Speter CVSroot_username = NULL; 38825839Speter } 38925839Speter 39025839Speter if ((p = strchr (cvsroot_copy, ':'))) 39125839Speter { 39225839Speter CVSroot_hostname = cvsroot_copy; 39325839Speter *p = '\0'; 39425839Speter cvsroot_copy = ++p; 39525839Speter 39625839Speter if (*CVSroot_hostname == '\0') 39725839Speter CVSroot_hostname = NULL; 39825839Speter } 39925839Speter } 40025839Speter 40125839Speter CVSroot_directory = cvsroot_copy; 40225839Speter 40325839Speter#if ! defined (CLIENT_SUPPORT) && ! defined (DEBUG) 40425839Speter if (CVSroot_method != local_method) 40525839Speter { 40625839Speter error (0, 0, "Your CVSROOT is set for a remote access method"); 40725839Speter error (0, 0, "but your CVS executable doesn't support it"); 40825839Speter error (0, 0, "(%s)", CVSroot); 40925839Speter return 1; 41025839Speter } 41125839Speter#endif 41225839Speter 41325839Speter /* Do various sanity checks. */ 41425839Speter 41525839Speter if (CVSroot_username && ! CVSroot_hostname) 41625839Speter { 41725839Speter error (0, 0, "missing hostname in CVSROOT: %s", CVSroot); 41825839Speter return 1; 41925839Speter } 42025839Speter 42132785Speter check_hostname = 0; 42225839Speter switch (CVSroot_method) 42325839Speter { 42425839Speter case local_method: 42554427Speter case fork_method: 42625839Speter if (CVSroot_username || CVSroot_hostname) 42725839Speter { 42825839Speter error (0, 0, "can't specify hostname and username in CVSROOT"); 42954427Speter error (0, 0, "when using %s access method", 43054427Speter CVSroot_method == local_method ? "local" : "fork"); 43125839Speter error (0, 0, "(%s)", CVSroot); 43225839Speter return 1; 43325839Speter } 43425839Speter /* cvs.texinfo has always told people that CVSROOT must be an 43525839Speter absolute pathname. Furthermore, attempts to use a relative 43625839Speter pathname produced various errors (I couldn't get it to work), 43725839Speter so there would seem to be little risk in making this a fatal 43825839Speter error. */ 43925839Speter if (!isabsolute (CVSroot_directory)) 44025839Speter error (1, 0, "CVSROOT %s must be an absolute pathname", 44125839Speter CVSroot_directory); 44225839Speter break; 44325839Speter case kserver_method: 44425839Speter#ifndef HAVE_KERBEROS 44525839Speter error (0, 0, "Your CVSROOT is set for a kerberos access method"); 44625839Speter error (0, 0, "but your CVS executable doesn't support it"); 44725839Speter error (0, 0, "(%s)", CVSroot); 44825839Speter return 1; 44954427Speter#else 45032785Speter check_hostname = 1; 45132785Speter break; 45254427Speter#endif 45332785Speter case gserver_method: 45432785Speter#ifndef HAVE_GSSAPI 45532785Speter error (0, 0, "Your CVSROOT is set for a GSSAPI access method"); 45632785Speter error (0, 0, "but your CVS executable doesn't support it"); 45732785Speter error (0, 0, "(%s)", CVSroot); 45832785Speter return 1; 45954427Speter#else 46032785Speter check_hostname = 1; 46132785Speter break; 46254427Speter#endif 46325839Speter case server_method: 46425839Speter case ext_method: 46525839Speter case pserver_method: 46632785Speter check_hostname = 1; 46732785Speter break; 46832785Speter } 46932785Speter 47032785Speter if (check_hostname) 47132785Speter { 47225839Speter if (! CVSroot_hostname) 47325839Speter { 47425839Speter error (0, 0, "didn't specify hostname in CVSROOT: %s", CVSroot); 47525839Speter return 1; 47625839Speter } 47725839Speter } 47825839Speter 47925839Speter if (*CVSroot_directory == '\0') 48025839Speter { 48125839Speter error (0, 0, "missing directory in CVSROOT: %s", CVSroot); 48225839Speter return 1; 48325839Speter } 48425839Speter 48525839Speter /* Hooray! We finally parsed it! */ 48625839Speter return 0; 48725839Speter} 48825839Speter 48925839Speter 49025839Speter/* Set up the global CVSroot* variables as if we're using the local 49154427Speter repository DIR. DIR must point to storage which will last for the 49254427Speter rest of the CVS invocation (for example, the caller might malloc it 49354427Speter and never free it, or free it just before exiting CVS). */ 49425839Speter 49525839Spetervoid 49625839Speterset_local_cvsroot (dir) 49725839Speter char *dir; 49825839Speter{ 49954427Speter CVSroot_original = dir; 50025839Speter CVSroot_method = local_method; 50125839Speter CVSroot_directory = CVSroot_original; 50225839Speter CVSroot_username = NULL; 50325839Speter CVSroot_hostname = NULL; 50425839Speter client_active = 0; 50525839Speter} 50625839Speter 50725839Speter 50825839Speter#ifdef DEBUG 50954427Speter/* This is for testing the parsing function. Use 51025839Speter 51154427Speter gcc -I. -I.. -I../lib -DDEBUG root.c -o root 51254427Speter 51354427Speter to compile. */ 51454427Speter 51525839Speter#include <stdio.h> 51625839Speter 51725839Speterchar *CVSroot; 51825839Speterchar *program_name = "testing"; 51925839Speterchar *command_name = "parse_cvsroot"; /* XXX is this used??? */ 52025839Speter 52154427Speter/* Toy versions of various functions when debugging under unix. Yes, 52254427Speter these make various bad assumptions, but they're pretty easy to 52354427Speter debug when something goes wrong. */ 52454427Speter 52525839Spetervoid 52654427Spetererror_exit PROTO ((void)) 52754427Speter{ 52854427Speter exit (1); 52954427Speter} 53054427Speter 53154427Speterchar * 53254427Speterxstrdup (str) 53354427Speter const char *str; 53454427Speter{ 53554427Speter return strdup (str); 53654427Speter} 53754427Speter 53854427Speterint 53954427Speterisabsolute (dir) 54054427Speter const char *dir; 54154427Speter{ 54254427Speter return (dir && (*dir == '/')); 54354427Speter} 54454427Speter 54554427Spetervoid 54625839Spetermain (argc, argv) 54725839Speter int argc; 54825839Speter char *argv[]; 54925839Speter{ 55025839Speter program_name = argv[0]; 55125839Speter 55225839Speter if (argc != 2) 55325839Speter { 55425839Speter fprintf (stderr, "Usage: %s <CVSROOT>\n", program_name); 55525839Speter exit (2); 55625839Speter } 55725839Speter 55825839Speter if (parse_cvsroot (argv[1])) 55925839Speter { 56054427Speter fprintf (stderr, "%s: Parsing failed.\n", program_name); 56125839Speter exit (1); 56225839Speter } 56325839Speter printf ("CVSroot: %s\n", argv[1]); 56425839Speter printf ("CVSroot_method: %s\n", method_names[CVSroot_method]); 56525839Speter printf ("CVSroot_username: %s\n", 56625839Speter CVSroot_username ? CVSroot_username : "NULL"); 56725839Speter printf ("CVSroot_hostname: %s\n", 56825839Speter CVSroot_hostname ? CVSroot_hostname : "NULL"); 56925839Speter printf ("CVSroot_directory: %s\n", CVSroot_directory); 57025839Speter 57125839Speter exit (0); 57225839Speter /* NOTREACHED */ 57325839Speter} 57425839Speter#endif 575