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