From 2cd25f574496fdf67ca195bfc1c2cae7132605e3 Mon Sep 17 00:00:00 2001 From: christos Date: Sat, 4 Feb 2006 16:29:55 +0000 Subject: [PATCH] Add all our changes: - our kerberos support - nolock - setuid support / setxid in child - avoid dangerous commands and only allow admin group to execute them - selectable CVS directory name - symlinked repository fixes - log admin commands in history - default to ssh instead of rsh - localid keyword - null revision on re-added files - umask fixes - t flag in log - don't recursively re-enter signal error handler - xasprintf in selected places - ipv6 support --- gnu/dist/xcvs/src/admin.c | 185 ++++++++++++++++++++++++--------- gnu/dist/xcvs/src/checkout.c | 2 +- gnu/dist/xcvs/src/client.c | 118 +++++++++++---------- gnu/dist/xcvs/src/commit.c | 21 ++-- gnu/dist/xcvs/src/cvs.h | 73 +++++++------ gnu/dist/xcvs/src/diff.c | 6 +- gnu/dist/xcvs/src/edit.c | 10 +- gnu/dist/xcvs/src/entries.c | 46 +++----- gnu/dist/xcvs/src/filesubr.c | 33 +++++- gnu/dist/xcvs/src/find_names.c | 6 +- gnu/dist/xcvs/src/history.c | 3 +- gnu/dist/xcvs/src/history.h | 2 +- gnu/dist/xcvs/src/ignore.c | 12 ++- gnu/dist/xcvs/src/lock.c | 56 +++++++++- gnu/dist/xcvs/src/logmsg.c | 13 ++- gnu/dist/xcvs/src/main.c | 43 +++++++- gnu/dist/xcvs/src/mkmodules.c | 7 ++ gnu/dist/xcvs/src/modules.c | 3 +- gnu/dist/xcvs/src/parseinfo.c | 9 ++ gnu/dist/xcvs/src/patch.c | 5 + gnu/dist/xcvs/src/rcs.c | 18 +++- gnu/dist/xcvs/src/recurse.c | 6 +- gnu/dist/xcvs/src/remove.c | 6 +- gnu/dist/xcvs/src/repos.c | 10 +- gnu/dist/xcvs/src/root.c | 11 +- gnu/dist/xcvs/src/run.c | 7 ++ gnu/dist/xcvs/src/server.c | 37 +++++-- gnu/dist/xcvs/src/subr.c | 16 +++ gnu/dist/xcvs/src/tag.c | 2 - gnu/dist/xcvs/src/update.c | 36 +++---- 30 files changed, 528 insertions(+), 274 deletions(-) diff --git a/gnu/dist/xcvs/src/admin.c b/gnu/dist/xcvs/src/admin.c index 186e27c9dc8b..05c40758dac8 100644 --- a/gnu/dist/xcvs/src/admin.c +++ b/gnu/dist/xcvs/src/admin.c @@ -28,12 +28,16 @@ static int admin_fileproc PROTO ((void *callerdat, struct file_info *finfo)); static const char *const admin_usage[] = { "Usage: %s %s [options] files...\n", +#ifndef CVS_ADMIN_LIMITED "\t-a users Append (comma-separated) user names to access list.\n", "\t-A file Append another file's access list.\n", "\t-b[rev] Set default branch (highest branch on trunk if omitted).\n", +#endif "\t-c string Set comment leader.\n", +#ifndef CVS_ADMIN_LIMITED "\t-e[users] Remove (comma-separated) user names from access list\n", "\t (all names if omitted).\n", +#endif "\t-I Run interactively.\n", "\t-k subst Set keyword substitution mode:\n", "\t kv (Default) Substitute keyword and value.\n", @@ -42,10 +46,13 @@ static const char *const admin_usage[] = "\t o Preserve original string.\n", "\t b Like o, but mark file as binary.\n", "\t v Substitute value only.\n", +#ifndef CVS_ADMIN_LIMITED "\t-l[rev] Lock revision (latest revision on branch,\n", "\t latest revision on trunk if omitted).\n", "\t-L Set strict locking.\n", +#endif "\t-m rev:msg Replace revision's log message.\n", +#ifndef CVS_ADMIN_LIMITED "\t-n tag[:[rev]] Tag branch or revision. If :rev is omitted,\n", "\t delete the tag; if rev is omitted, tag the latest\n", "\t revision on the default branch.\n", @@ -58,14 +65,19 @@ static const char *const admin_usage[] = "\t :rev rev and previous revisions on the same branch.\n", "\t ::rev Before rev on the same branch.\n", "\t rev Just rev.\n", +#endif "\t-q Run quietly.\n", +#ifndef CVS_ADMIN_LIMITED "\t-s state[:rev] Set revision state (latest revision on branch,\n", "\t latest revision on trunk if omitted).\n", +#endif "\t-t[file] Get descriptive text from file (stdin if omitted).\n", "\t-t-string Set descriptive text.\n", +#ifndef CVS_ADMIN_LIMITED "\t-u[rev] Unlock the revision (latest revision on branch,\n", "\t latest revision on trunk if omitted).\n", "\t-U Unset strict locking.\n", +#endif "(Specify the --help global option for a list of other help options)\n", NULL }; @@ -110,6 +122,11 @@ struct admin_data int ac; char **av; int av_alloc; + + /* This contains a printable version of the command line used + * for logging + */ + char *cmdline; }; /* Add an argument. OPT is the option letter, e.g. 'a'. ARG is the @@ -144,20 +161,113 @@ arg_add (dat, opt, arg) dat->av[dat->ac++] = newelt; } +static size_t +wescape(dst, src) + char *dst; + const char *src; +{ + const unsigned char *s = src; + char *d = dst; + for (; *s; s++) { + if (!isprint(*s) || isspace(*s) || *s == '|') { + *d++ = '\\'; + *d++ = ((*s >> 6) & 3) + '0'; + *d++ = ((*s >> 3) & 7) + '0'; + *d++ = ((*s >> 0) & 7) + '0'; + } else { + *d++ = *s; + } + } + *d = '\0'; + return d - dst; +} + +static char * +makecmdline(argc, argv) + int argc; + char **argv; +{ + size_t clen = 1024, wlen = 1024, len, cpos = 0, i; + char *cmd = xmalloc(clen); + char *word = xmalloc(wlen); + + for (i = 0; i < argc; i++) { + char *arg = (strncmp(argv[i], "cvs ", 4) == 0) ? argv[i] + 4 : argv[i]; + len = strlen(arg); + if (len * 4 < wlen) { + wlen += len * 4; + word = xrealloc(word, wlen); + } + len = wescape(word, arg); + if (clen - cpos < len + 2) { + clen += len + 2; + cmd = xrealloc(cmd, clen); + } + memcpy(&cmd[cpos], word, len); + cpos += len; + cmd[cpos++] = ' '; + } + if (cpos != 0) + cmd[cpos - 1] = '\0'; + else + cmd[cpos] = '\0'; + free(word); + return cmd; +} + +int +admin_group_member() +{ + struct group *grp; + struct group *getgrnam(); + int i; + + if (CVS_admin_group == NULL) + return 1; + + if ((grp = getgrnam(CVS_admin_group)) == NULL) + return 0; + + { +#ifdef HAVE_GETGROUPS + gid_t *grps; + int n; + + /* get number of auxiliary groups */ + n = getgroups (0, NULL); + if (n < 0) + error (1, errno, "unable to get number of auxiliary groups"); + grps = (gid_t *) xmalloc((n + 1) * sizeof *grps); + n = getgroups (n, grps); + if (n < 0) + error (1, errno, "unable to get list of auxiliary groups"); + grps[n] = getgid(); + for (i = 0; i <= n; i++) + if (grps[i] == grp->gr_gid) break; + free (grps); + if (i > n) + return 0; +#else + char *me = getcaller(); + char **grnam; + + for (grnam = grp->gr_mem; *grnam; grnam++) + if (strcmp (*grnam, me) == 0) break; + if (!*grnam && getgid() != grp->gr_gid) + return 0; +#endif + } +} int admin (argc, argv) int argc; char **argv; { int err; -#ifdef CVS_ADMIN_GROUP - struct group *grp; - struct group *getgrnam(); -#endif struct admin_data admin_data; int c; int i; - int only_k_option; + int only_limited_options = 1; if (argc <= 1) usage (admin_usage); @@ -165,17 +275,17 @@ admin (argc, argv) wrap_setup (); memset (&admin_data, 0, sizeof admin_data); + admin_data.cmdline = makecmdline (argc, argv); /* TODO: get rid of `-' switch notation in admin_data. For example, admin_data->branch should be not `-bfoo' but simply `foo'. */ optind = 0; - only_k_option = 1; while ((c = getopt (argc, argv, "+ib::c:a:A:e::l::u::LUn:N:m:o:s:t::IqxV:k:")) != -1) { - if (c != 'k' && c != 'q') - only_k_option = 0; + if (CVS_admin_options == NULL || strchr(CVS_admin_options, c) == NULL) + only_limited_options = 0; switch (c) { @@ -188,6 +298,7 @@ admin (argc, argv) goto usage_error; case 'b': + if (admin_data.branch != NULL) { error (0, 0, "duplicate 'b' option"); @@ -380,49 +491,17 @@ admin (argc, argv) argc -= optind; argv += optind; -#ifdef CVS_ADMIN_GROUP - /* The use of `cvs admin -k' is unrestricted. However, any other - option is restricted if the group CVS_ADMIN_GROUP exists on the - server. */ - /* This is only "secure" on the server, since the user could edit the - * RCS file on a local host, but some people like this kind of - * check anyhow. The alternative would be to check only when - * (server_active) rather than when not on the client. - */ - if (!current_parsed_root->isremote && !only_k_option && - (grp = getgrnam(CVS_ADMIN_GROUP)) != NULL) - { -#ifdef HAVE_GETGROUPS - gid_t *grps; - int n; - - /* get number of auxiliary groups */ - n = getgroups (0, NULL); - if (n < 0) - error (1, errno, "unable to get number of auxiliary groups"); - grps = (gid_t *) xmalloc((n + 1) * sizeof *grps); - n = getgroups (n, grps); - if (n < 0) - error (1, errno, "unable to get list of auxiliary groups"); - grps[n] = getgid(); - for (i = 0; i <= n; i++) - if (grps[i] == grp->gr_gid) break; - free (grps); - if (i > n) - error (1, 0, "usage is restricted to members of the group %s", - CVS_ADMIN_GROUP); -#else - char *me = getcaller(); - char **grnam; - - for (grnam = grp->gr_mem; *grnam; grnam++) - if (strcmp (*grnam, me) == 0) break; - if (!*grnam && getgid() != grp->gr_gid) - error (1, 0, "usage is restricted to members of the group %s", - CVS_ADMIN_GROUP); -#endif - } -#endif /* defined CVS_ADMIN_GROUP */ + if ( + /* This is only "secure" on the server, since the user could edit the + * RCS file on a local host, but some people like this kind of + * check anyhow. The alternative would be to check only when + * (server_active) rather than when not on the client. + */ + !current_parsed_root->isremote && + !only_limited_options && + !admin_group_member()) + error (1, 0, "usage is restricted to members of the group %s", + CVS_admin_group); for (i = 0; i < admin_data.ac; ++i) { @@ -526,6 +605,8 @@ admin (argc, argv) Lock_Cleanup (); return_it: + if (admin_data.cmdline != NULL) + free (admin_data.cmdline); if (admin_data.branch != NULL) free (admin_data.branch); if (admin_data.comment != NULL) @@ -570,6 +651,8 @@ admin_fileproc (callerdat, finfo) goto exitfunc; } + history_write ('X', finfo->update_dir, admin_data->cmdline, finfo->file, + finfo->repository); rcs = vers->srcfile; if (rcs == NULL) { diff --git a/gnu/dist/xcvs/src/checkout.c b/gnu/dist/xcvs/src/checkout.c index e4d80eaddd24..d20d10af68ff 100644 --- a/gnu/dist/xcvs/src/checkout.c +++ b/gnu/dist/xcvs/src/checkout.c @@ -194,7 +194,7 @@ checkout (argc, argv) case 'p': pipeout = 1; run_module_prog = 0; /* don't run module prog when piping */ - noexec = 1; /* so no locks will be created */ + noexec = nolock = 1; /* so no locks will be created */ break; case 'c': cat = 1; diff --git a/gnu/dist/xcvs/src/client.c b/gnu/dist/xcvs/src/client.c index c298938e10c9..2571d93a5d93 100644 --- a/gnu/dist/xcvs/src/client.c +++ b/gnu/dist/xcvs/src/client.c @@ -81,7 +81,7 @@ static Key_schedule sched; /* This is needed for GSSAPI encryption. */ static gss_ctx_id_t gcontext; -static int connect_to_gserver PROTO((cvsroot_t *, int, struct hostent *)); +static int connect_to_gserver PROTO((cvsroot_t *, int, const char *)); # endif /* HAVE_GSSAPI */ @@ -145,7 +145,7 @@ static void handle_notified PROTO((char *, int)); static size_t try_read_from_server PROTO ((char *, size_t)); static void auth_server PROTO ((cvsroot_t *, struct buffer *, struct buffer *, - int, int, struct hostent *)); + int, int)); /* We need to keep track of the list of directories we've sent to the server. This list, along with the current CVSROOT, will help us @@ -1508,7 +1508,7 @@ handle_copy_file (args, len) } -static void read_counted_file PROTO ((char *, char *)); +static void read_counted_file PROTO ((const char *, const char *)); /* Read from the server the count for the length of a file, then read the contents of that file and write them to FILENAME. FULLNAME is @@ -1517,8 +1517,8 @@ static void read_counted_file PROTO ((char *, char *)); use it. On error, gives a fatal error. */ static void read_counted_file (filename, fullname) - char *filename; - char *fullname; + const char *filename; + const char *fullname; { char *size_string; size_t size; @@ -3800,33 +3800,50 @@ connect_to_pserver (root, to_server_p, from_server_p, verify_only, do_gssapi) { int sock; int port_number; - struct sockaddr_in client_sai; - struct hostent *hostinfo; + char no_passwd = 0; /* gets set if no password found */ + struct addrinfo hints, *res, *res0 = NULL; + char pbuf[10]; + int e; struct buffer *to_server, *from_server; - sock = socket (AF_INET, SOCK_STREAM, 0); - if (sock == -1) - { - error (1, 0, "cannot create socket: %s", SOCK_STRERROR (SOCK_ERRNO)); - } + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_CANONNAME; port_number = get_cvs_port_number (root); - hostinfo = init_sockaddr (&client_sai, root->hostname, port_number); - if (trace) + snprintf(pbuf, sizeof(pbuf), "%d", port_number); + e = getaddrinfo(root->hostname, pbuf, &hints, &res0); + if (e) { - fprintf (stderr, " -> Connecting to %s(%s):%d\n", - root->hostname, - inet_ntoa (client_sai.sin_addr), port_number); + error (1, 0, "%s", gai_strerror(e)); + } + sock = -1; + for (res = res0; res; res = res->ai_next) { + sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (sock < 0) + continue; + + if (trace) + { + fprintf (stderr, " -> Connecting to %s\n", root->hostname); + } + if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) { + close(sock); + sock = -1; + continue; + } + break; + } + freeaddrinfo(res0); + if (sock < 0) + { + error (1, 0, "connect to %s:%s failed: %s", root->hostname, + pbuf, SOCK_STRERROR (SOCK_ERRNO)); } - if (connect (sock, (struct sockaddr *) &client_sai, sizeof (client_sai)) - < 0) - error (1, 0, "connect to %s(%s):%d failed: %s", - root->hostname, - inet_ntoa (client_sai.sin_addr), - port_number, SOCK_STRERROR (SOCK_ERRNO)); make_bufs_from_fds (sock, sock, 0, &to_server, &from_server, 1); - auth_server (root, to_server, from_server, verify_only, do_gssapi, hostinfo); + auth_server (root, to_server, from_server, verify_only, do_gssapi); if (verify_only) { @@ -3860,13 +3877,12 @@ connect_to_pserver (root, to_server_p, from_server_p, verify_only, do_gssapi) static void -auth_server (root, lto_server, lfrom_server, verify_only, do_gssapi, hostinfo) +auth_server (root, lto_server, lfrom_server, verify_only, do_gssapi) cvsroot_t *root; struct buffer *lto_server; struct buffer *lfrom_server; int verify_only; int do_gssapi; - struct hostent *hostinfo; { char *username; /* the username we use to connect */ char no_passwd = 0; /* gets set if no password found */ @@ -3896,7 +3912,7 @@ auth_server (root, lto_server, lfrom_server, verify_only, do_gssapi, hostinfo) error (1, 0, "gserver currently only enabled for socket connections"); } - if (! connect_to_gserver (root, fd, hostinfo)) + if (! connect_to_gserver (root, fd, root->hostname)) { error (1, 0, "authorization failed: server %s rejected access to %s", @@ -4137,7 +4153,8 @@ start_tcp_server (root, to_server, from_server) /* We don't care about the checksum, and pass it as zero. */ status = krb_sendauth (KOPT_DO_MUTUAL, s, &ticket, "rcmd", - hname, realm, (unsigned long) 0, &msg_data, + hname, (char *)realm, (unsigned long) 0, + &msg_data, &cred, sched, &laddr, &sin, "KCVSV1.0"); if (status != KSUCCESS) error (1, 0, "kerberos authentication failed: %s", @@ -4197,10 +4214,10 @@ recv_bytes (sock, buf, need) */ #define BUFSIZE 1024 static int -connect_to_gserver (root, sock, hostinfo) +connect_to_gserver (root, sock, hostname) cvsroot_t *root; int sock; - struct hostent *hostinfo; + const char *hostname; { char *str; char buf[BUFSIZE]; @@ -4213,9 +4230,9 @@ connect_to_gserver (root, sock, hostinfo) if (send (sock, str, strlen (str), 0) < 0) error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO)); - if (strlen (hostinfo->h_name) > BUFSIZE - 5) + if (strlen (hostname) > BUFSIZE - 5) error (1, 0, "Internal error: hostname exceeds length of buffer"); - sprintf (buf, "cvs@%s", hostinfo->h_name); + sprintf (buf, "cvs@%s", hostname); tok_in.length = strlen (buf); tok_in.value = buf; gss_import_name (&stat_min, &tok_in, GSS_C_NT_HOSTBASED_SERVICE, @@ -4528,6 +4545,16 @@ start_server () error (1, 0, "This server does not support the global -n option."); } + if (nolock && !noexec) + { + if (have_global) + { + send_to_server ("Global_option -u\012", 0); + } + else + error (1, 0, + "This server does not support the global -u option."); + } if (quiet) { if (have_global) @@ -4759,27 +4786,7 @@ start_rsh_server (root, to_server, from_server) char *rsh_argv[10]; if (!cvs_rsh) - /* People sometimes suggest or assume that this should default - to "remsh" on systems like HPUX in which that is the - system-supplied name for the rsh program. However, that - causes various problems (keep in mind that systems such as - HPUX might have non-system-supplied versions of "rsh", like - a Kerberized one, which one might want to use). If we - based the name on what is found in the PATH of the person - who runs configure, that would make it harder to - consistently produce the same result in the face of - different people producing binary distributions. If we - based it on "remsh" always being the default for HPUX - (e.g. based on uname), that might be slightly better but - would require us to keep track of what the defaults are for - each system type, and probably would cope poorly if the - existence of remsh or rsh varies from OS version to OS - version. Therefore, it seems best to have the default - remain "rsh", and tell HPUX users to specify remsh, for - example in CVS_RSH or other such mechanisms to be devised, - if that is what they want (the manual already tells them - that). */ - cvs_rsh = "rsh"; + cvs_rsh = "ssh"; if (!cvs_server) cvs_server = "cvs"; @@ -4840,7 +4847,7 @@ start_rsh_server (root, to_server, from_server) int child_pid; if (!cvs_rsh) - cvs_rsh = "rsh"; + cvs_rsh = "ssh"; if (!cvs_server) cvs_server = "cvs"; @@ -5332,8 +5339,7 @@ send_dirent_proc (callerdat, dir, repository, update_dir, entries) * This case will happen when checking out a module defined as * ``-a .''. */ - cvsadm_name = xmalloc (strlen (dir) + sizeof (CVSADM) + 10); - sprintf (cvsadm_name, "%s/%s", dir, CVSADM); + xasprintf (&cvsadm_name, "%s/%s", dir, CVSADM); dir_exists = isdir (cvsadm_name); free (cvsadm_name); diff --git a/gnu/dist/xcvs/src/commit.c b/gnu/dist/xcvs/src/commit.c index 48088b74cb17..d57e23c75cb8 100644 --- a/gnu/dist/xcvs/src/commit.c +++ b/gnu/dist/xcvs/src/commit.c @@ -1024,7 +1024,9 @@ warning: file `%s' seems to still contain conflict indicators", xmalloc (sizeof (struct logfile_info))); li->type = status; li->tag = xstrdup (vers->tag); - li->rev_old = xstrdup (vers->vn_rcs); + /* If the file was re-added, we want the revision in the commitlog + to be NONE, not the previous dead revision. */ + li->rev_old = status == T_ADDED ? NULL : xstrdup (vers->vn_rcs); li->rev_new = NULL; p->data = li; (void) addnode (ulist, p); @@ -1176,7 +1178,8 @@ precommit_proc (repository, filter) run_setup (filter); run_arg (repository); (void) walklist (saved_ulist, precommit_list_proc, NULL); - return run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY); + return run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY| + (server_active ? 0 : RUN_UNSETXID)); } @@ -1787,9 +1790,8 @@ finaladd (finfo, rev, tag, options) ret = Checkin ('A', finfo, rev, tag, options, saved_message); if (ret == 0) { - char *tmp = xmalloc (strlen (finfo->file) + sizeof (CVSADM) - + sizeof (CVSEXT_LOG) + 10); - (void) sprintf (tmp, "%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG); + char *tmp; + (void) xasprintf (&tmp, "%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG); if (unlink_file (tmp) < 0 && !existence_error (errno)) error (0, errno, "cannot remove %s", tmp); @@ -1970,9 +1972,8 @@ checkaddfile (file, repository, tag, options, rcsnode) /* this is the first time we have ever seen this file; create an RCS file. */ - fname = xmalloc (strlen (file) + sizeof (CVSADM) - + sizeof (CVSEXT_LOG) + 10); - (void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_LOG); + + (void) xasprintf (&fname, "%s/%s%s", CVSADM, file, CVSEXT_LOG); /* If the file does not exist, no big deal. In particular, the server does not (yet at least) create CVSEXT_LOG files. */ if (isfile (fname)) @@ -2090,9 +2091,7 @@ checkaddfile (file, repository, tag, options, rcsnode) int retcode; /* move the new file out of the way. */ - fname = xmalloc (strlen (file) + sizeof (CVSADM) - + sizeof (CVSPREFIX) + 10); - (void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, file); + (void) xasprintf (&fname, "%s/%s%s", CVSADM, CVSPREFIX, file); rename_file (file, fname); /* Create empty FILE. Can't use copy_file with a DEVNULL diff --git a/gnu/dist/xcvs/src/cvs.h b/gnu/dist/xcvs/src/cvs.h index f7649d485baf..82dc8a8808f5 100644 --- a/gnu/dist/xcvs/src/cvs.h +++ b/gnu/dist/xcvs/src/cvs.h @@ -130,38 +130,42 @@ extern int errno; Here as #define's to make changing the names a simple task. */ #ifdef USE_VMS_FILENAMES -#define CVSADM "CVS" -#define CVSADM_ENT "CVS/Entries." -#define CVSADM_ENTBAK "CVS/Entries.Backup" -#define CVSADM_ENTLOG "CVS/Entries.Log" -#define CVSADM_ENTSTAT "CVS/Entries.Static" -#define CVSADM_REP "CVS/Repository." -#define CVSADM_ROOT "CVS/Root." -#define CVSADM_TAG "CVS/Tag." -#define CVSADM_NOTIFY "CVS/Notify." -#define CVSADM_NOTIFYTMP "CVS/Notify.tmp" -#define CVSADM_BASE "CVS/Base" -#define CVSADM_BASEREV "CVS/Baserev." -#define CVSADM_BASEREVTMP "CVS/Baserev.tmp" -#define CVSADM_TEMPLATE "CVS/Template." +#define CVSADM getCVSDir("") +#define CVSADM_ENT getCVSDir("/Entries.") +#define CVSADM_ENTBAK getCVSDir("/Entries.Backup") +#define CVSADM_ENTLOG getCVSDir("/Entries.Log") +#define CVSADM_ENTSTAT getCVSDir("/Entries.Static") +#define CVSADM_REP getCVSDir("/Repository.") +#define CVSADM_ROOT getCVSDir("/Root.") +#define CVSADM_CIPROG getCVSDir("/Checkin.prog") +#define CVSADM_UPROG getCVSDir("/Update.prog") +#define CVSADM_TAG getCVSDir("/Tag.") +#define CVSADM_NOTIFY getCVSDir("/Notify.") +#define CVSADM_NOTIFYTMP getCVSDir("/Notify.tmp") +#define CVSADM_BASE getCVSDir("/Base") +#define CVSADM_BASEREV getCVSDir("/Baserev.") +#define CVSADM_BASEREVTMP getCVSDir("/Baserev.tmp") +#define CVSADM_TEMPLATE getCVSDir("/Template.") #else /* USE_VMS_FILENAMES */ -#define CVSADM "CVS" -#define CVSADM_ENT "CVS/Entries" -#define CVSADM_ENTBAK "CVS/Entries.Backup" -#define CVSADM_ENTLOG "CVS/Entries.Log" -#define CVSADM_ENTSTAT "CVS/Entries.Static" -#define CVSADM_REP "CVS/Repository" -#define CVSADM_ROOT "CVS/Root" -#define CVSADM_TAG "CVS/Tag" -#define CVSADM_NOTIFY "CVS/Notify" -#define CVSADM_NOTIFYTMP "CVS/Notify.tmp" +#define CVSADM getCVSDir("") +#define CVSADM_ENT getCVSDir("/Entries") +#define CVSADM_ENTBAK getCVSDir("/Entries.Backup") +#define CVSADM_ENTLOG getCVSDir("/Entries.Log") +#define CVSADM_ENTSTAT getCVSDir("/Entries.Static") +#define CVSADM_REP getCVSDir("/Repository") +#define CVSADM_ROOT getCVSDir("/Root") +#define CVSADM_CIPROG getCVSDir("/Checkin.prog") +#define CVSADM_UPROG getCVSDir("/Update.prog") +#define CVSADM_TAG getCVSDir("/Tag") +#define CVSADM_NOTIFY getCVSDir("/Notify") +#define CVSADM_NOTIFYTMP getCVSDir("/Notify.tmp") /* A directory in which we store base versions of files we currently are editing with "cvs edit". */ -#define CVSADM_BASE "CVS/Base" -#define CVSADM_BASEREV "CVS/Baserev" -#define CVSADM_BASEREVTMP "CVS/Baserev.tmp" +#define CVSADM_BASE getCVSDir("/Base") +#define CVSADM_BASEREV getCVSDir("/Baserev") +#define CVSADM_BASEREVTMP getCVSDir("/Baserev.tmp") /* File which contains the template for use in log messages. */ -#define CVSADM_TEMPLATE "CVS/Template" +#define CVSADM_TEMPLATE getCVSDir("/Template") #endif /* USE_VMS_FILENAMES */ /* This is the special directory which we use to store various extra @@ -172,7 +176,7 @@ extern int errno; See fileattr.h for details about file attributes, the only thing stored in CVSREP currently. */ -#define CVSREP "CVS" +#define CVSREP getCVSDir("") /* * Definitions for the CVSROOT Administrative directory and the files it @@ -382,8 +386,10 @@ extern int really_quiet, quiet; extern int use_editor; extern int cvswrite; extern mode_t cvsumask; - - +extern char *RCS_citag; +extern char *CVS_admin_group; +extern char *CVS_admin_options; +extern int admin_group_member PROTO((void)); /* This global variable holds the global -d option. It is NULL if -d was not used, which means that we must get the CVSroot information @@ -400,6 +406,7 @@ extern int safe_location PROTO ((char *)); extern int trace; /* Show all commands */ extern int noexec; /* Don't modify disk anywhere */ +extern int nolock; /* Don't create locks */ extern int logoff; /* Don't write history entry */ extern int top_level_admin; @@ -453,6 +460,7 @@ List *Entries_Open PROTO ((int aflag, char *update_dir)); void Subdirs_Known PROTO((List *entries)); void Subdir_Register PROTO((List *, const char *, const char *)); void Subdir_Deregister PROTO((List *, const char *, const char *)); +const char *getCVSDir PROTO((const char *)); char *Make_Date PROTO((char *rawdate)); char *date_from_time_t PROTO ((time_t)); @@ -475,6 +483,8 @@ void *xrealloc PROTO((void *ptr, size_t bytes)); void expand_string PROTO ((char **, size_t *, size_t)); void xrealloc_and_strcat PROTO ((char **, size_t *, const char *)); char *xstrdup PROTO((const char *str)); +int xasprintf PROTO((char ** __restrict, const char * __restrict, ...)) + __attribute__((__format__(__printf__, 2, 3))); int strip_trailing_newlines PROTO((char *str)); int pathname_levels PROTO ((const char *path)); @@ -694,6 +704,7 @@ void sleep_past PROTO ((time_t desttime)); #define RUN_STDOUT_APPEND 0x0004 /* append to stdout, don't truncate */ #define RUN_STDERR_APPEND 0x0008 /* append to stderr, don't truncate */ #define RUN_SIGIGNORE 0x0010 /* ignore interrupts for command */ +#define RUN_UNSETXID 0x0020 /* undo setxid in child */ #define RUN_TTY (char *)0 /* for the benefit of lint */ void run_add_arg_p PROTO ((int *, size_t *, char ***, const char *s)); diff --git a/gnu/dist/xcvs/src/diff.c b/gnu/dist/xcvs/src/diff.c index a5ca2d0225bd..d941d2e40406 100644 --- a/gnu/dist/xcvs/src/diff.c +++ b/gnu/dist/xcvs/src/diff.c @@ -672,11 +672,7 @@ diff_fileproc (callerdat, finfo) if( tocvsPath != NULL ) { /* Backup the current version of the file to CVS/,,filename */ - fname = xmalloc (strlen (finfo->file) - + sizeof CVSADM - + sizeof CVSPREFIX - + 10); - sprintf(fname,"%s/%s%s",CVSADM, CVSPREFIX, finfo->file); + xasprintf(&fname,"%s/%s%s",CVSADM, CVSPREFIX, finfo->file); if (unlink_file_dir (fname) < 0) if (! existence_error (errno)) error (1, errno, "cannot remove %s", fname); diff --git a/gnu/dist/xcvs/src/edit.c b/gnu/dist/xcvs/src/edit.c index 754e196fc6d0..471085de0276 100644 --- a/gnu/dist/xcvs/src/edit.c +++ b/gnu/dist/xcvs/src/edit.c @@ -341,10 +341,7 @@ edit_fileproc (callerdat, finfo) trying to create the output file fails. But copy_file isn't set up to facilitate that. */ mkdir_if_needed (CVSADM_BASE); - basefilename = xmalloc (10 + sizeof CVSADM_BASE + strlen (finfo->file)); - strcpy (basefilename, CVSADM_BASE); - strcat (basefilename, "/"); - strcat (basefilename, finfo->file); + xasprintf(&basefilename, "%s/%s", CVSADM_BASE, finfo->file); copy_file (finfo->file, basefilename); free (basefilename); @@ -473,10 +470,7 @@ unedit_fileproc (callerdat, finfo) if (noexec) return 0; - basefilename = xmalloc (10 + sizeof CVSADM_BASE + strlen (finfo->file)); - strcpy (basefilename, CVSADM_BASE); - strcat (basefilename, "/"); - strcat (basefilename, finfo->file); + xasprintf(&basefilename, "%s/%s", CVSADM_BASE, finfo->file); if (!isfile (basefilename)) { /* This file apparently was never cvs edit'd (e.g. we are uneditting diff --git a/gnu/dist/xcvs/src/entries.c b/gnu/dist/xcvs/src/entries.c index c346fb67a228..b9275f834ac6 100644 --- a/gnu/dist/xcvs/src/entries.c +++ b/gnu/dist/xcvs/src/entries.c @@ -100,7 +100,7 @@ write_ent_proc (node, closure) Entnode *entnode = node->data; if (closure != NULL && entnode->type != ENT_FILE) - *(int *) closure = 1; + closure = (void *)1; if (fputentent(entfile, entnode)) error (1, errno, "cannot write %s", entfilename); @@ -121,7 +121,7 @@ write_entries (list) sawdir = 0; /* open the new one and walk the list writing entries */ - entfilename = CVSADM_ENTBAK; + entfilename = (char *)CVSADM_ENTBAK; entfile = CVS_FOPEN (entfilename, "w+"); if (entfile == NULL) { @@ -188,7 +188,7 @@ Scratch_Entry (list, fname) { if (!noexec) { - entfilename = CVSADM_ENTLOG; + entfilename = (char *)CVSADM_ENTLOG; entfile = open_file (entfilename, "a"); if (fprintf (entfile, "R ") < 0) @@ -251,7 +251,7 @@ Register (list, fname, vn, ts, options, tag, date, ts_conflict) if (!noexec) { - entfilename = CVSADM_ENTLOG; + entfilename = (char *)CVSADM_ENTLOG; entfile = CVS_FOPEN (entfilename, "a"); if (entfile == NULL) @@ -659,13 +659,10 @@ WriteTag (dir, tag, date, nonbranch, update_dir, repository) if (noexec) return; - tmp = xmalloc ((dir ? strlen (dir) : 0) - + sizeof (CVSADM_TAG) - + 10); if (dir == NULL) - (void) strcpy (tmp, CVSADM_TAG); + tmp = xstrdup(CVSADM_TAG); else - (void) sprintf (tmp, "%s/%s", dir, CVSADM_TAG); + (void) xasprintf (&tmp, "%s/%s", dir, CVSADM_TAG); if (tag || date) { @@ -816,7 +813,7 @@ Subdirs_Known (entries) if (!noexec) { /* Create Entries.Log so that Entries_Close will do something. */ - entfilename = CVSADM_ENTLOG; + entfilename = (char *)CVSADM_ENTLOG; fp = CVS_FOPEN (entfilename, "a"); if (fp == NULL) { @@ -856,14 +853,9 @@ subdir_record (cmd, parent, dir) if (!noexec) { if (parent == NULL) - entfilename = CVSADM_ENTLOG; + entfilename = (char *)CVSADM_ENTLOG; else - { - entfilename = xmalloc (strlen (parent) - + sizeof CVSADM_ENTLOG - + 10); - sprintf (entfilename, "%s/%s", parent, CVSADM_ENTLOG); - } + xasprintf (&entfilename, "%s/%s", parent, CVSADM_ENTLOG); entfile = CVS_FOPEN (entfilename, "a"); if (entfile == NULL) @@ -1030,23 +1022,15 @@ base_walk (code, finfo, rev) computation probably should be broken out into a separate function, as recurse.c does it too and places like Entries_Open should be doing it. */ - baserev_fullname = xmalloc (sizeof (CVSADM_BASEREV) - + strlen (finfo->update_dir) - + 2); - baserev_fullname[0] = '\0'; - baserevtmp_fullname = xmalloc (sizeof (CVSADM_BASEREVTMP) - + strlen (finfo->update_dir) - + 2); - baserevtmp_fullname[0] = '\0'; if (finfo->update_dir[0] != '\0') { - strcat (baserev_fullname, finfo->update_dir); - strcat (baserev_fullname, "/"); - strcat (baserevtmp_fullname, finfo->update_dir); - strcat (baserevtmp_fullname, "/"); + xasprintf(&baserev_fullname, "%s/%s", finfo->update_dir, CVSADM_BASEREV); + xasprintf(&baserevtmp_fullname, "%s/%s", + finfo->update_dir, CVSADM_BASEREVTMP); + } else { + baserev_fullname = xstrdup(CVSADM_BASEREV); + baserevtmp_fullname = xstrdup(CVSADM_BASEREVTMP); } - strcat (baserev_fullname, CVSADM_BASEREV); - strcat (baserevtmp_fullname, CVSADM_BASEREVTMP); fp = CVS_FOPEN (CVSADM_BASEREV, "r"); if (fp == NULL) diff --git a/gnu/dist/xcvs/src/filesubr.c b/gnu/dist/xcvs/src/filesubr.c index acece3fb4bc3..c0f29957222c 100644 --- a/gnu/dist/xcvs/src/filesubr.c +++ b/gnu/dist/xcvs/src/filesubr.c @@ -203,6 +203,37 @@ iswritable (file) return isaccessible(file, W_OK); } +#ifdef SETXID_SUPPORT +int +ingroup(gid_t gid) +{ + gid_t *gidp; + int i, ngroups; + + if (gid == getegid()) + return 1; + + ngroups = getgroups(0, NULL); + if (ngroups == -1) + return 0; + + if ((gidp = malloc(sizeof(gid_t) * ngroups)) == NULL) + return 0; + + if (getgroups(ngroups, gidp) == -1) { + free(gidp); + return 0; + } + + for (i = 0; i < ngroups; i++) + if (gid == gidp[i]) + break; + + free(gidp); + return i != ngroups; +} +#endif + /* * Returns non-zero if the argument file is accessable according to * mode. If compiled with SETXID_SUPPORT also works if cvs has setxid @@ -254,7 +285,7 @@ isaccessible (file, mode) omask |= S_IXOTH; } - mask = sb.st_uid == uid ? umask : sb.st_gid == getegid() ? gmask : omask; + mask = sb.st_uid == uid ? umask : ingroup(sb.st_gid) ? gmask : omask; if ((sb.st_mode & mask) == mask) return 1; errno = EACCES; diff --git a/gnu/dist/xcvs/src/find_names.c b/gnu/dist/xcvs/src/find_names.c index 5bfd8957ef15..7670f77e3b8d 100644 --- a/gnu/dist/xcvs/src/find_names.c +++ b/gnu/dist/xcvs/src/find_names.c @@ -366,7 +366,7 @@ find_dirs (dir, list, checkadm, entries) expand_string (&tmp, &tmp_size, strlen (dir) + strlen (dp->d_name) + 10); - sprintf (tmp, "%s/%s", dir, dp->d_name); + snprintf (tmp, tmp_size, "%s/%s", dir, dp->d_name); if (!isdir (tmp)) goto do_it_again; @@ -397,8 +397,8 @@ find_dirs (dir, list, checkadm, entries) expand_string (&tmp, &tmp_size, (strlen (dir) + strlen (dp->d_name) - + sizeof (CVSADM) + 10)); - (void) sprintf (tmp, "%s/%s/%s", dir, dp->d_name, CVSADM); + + strlen (CVSADM) + 10)); + (void) snprintf (tmp, tmp_size, "%s/%s/%s", dir, dp->d_name, CVSADM); if (!isdir (tmp)) goto do_it_again; } diff --git a/gnu/dist/xcvs/src/history.c b/gnu/dist/xcvs/src/history.c index d401003c5c3a..f6fe830632d5 100644 --- a/gnu/dist/xcvs/src/history.c +++ b/gnu/dist/xcvs/src/history.c @@ -36,6 +36,7 @@ * M "Commit" cmd - "Modified" file. * A "Commit" cmd - "Added" file. * R "Commit" cmd - "Removed" file. + * X "Admin" cmd. * * date is a fixed length 8-char hex representation of a Unix time_t. * [Starting here, variable fields are delimited by '|' chars.] @@ -760,7 +761,7 @@ history_write (type, update_dir, revs, name, repository) if (trace) fprintf (stderr, "%s-> fopen(%s,a)\n", CLIENT_SERVER_STR, fname); - if (noexec) + if (nolock) goto out; if (!history_lock (current_parsed_root->directory)) diff --git a/gnu/dist/xcvs/src/history.h b/gnu/dist/xcvs/src/history.h index dadc4214c60d..39c04d9ee08f 100644 --- a/gnu/dist/xcvs/src/history.h +++ b/gnu/dist/xcvs/src/history.h @@ -12,4 +12,4 @@ * with other portions of CVS. */ -#define ALL_HISTORY_REC_TYPES "TOEFWUPCGMAR" +#define ALL_HISTORY_REC_TYPES "TOEFWUPCGMARX" diff --git a/gnu/dist/xcvs/src/ignore.c b/gnu/dist/xcvs/src/ignore.c index 65b226038c5c..49c6437b5c17 100644 --- a/gnu/dist/xcvs/src/ignore.c +++ b/gnu/dist/xcvs/src/ignore.c @@ -33,8 +33,9 @@ static int ign_size; /* This many slots available (plus static int ign_hold = -1; /* Index where first "temporary" item * is held */ +extern const char *cvsDir; const char *ign_default = ". .. core RCSLOG tags TAGS RCS SCCS .make.state\ - .nse_depinfo #* .#* cvslog.* ,* CVS CVS.adm .del-* *.a *.olb *.o *.obj\ + .nse_depinfo #* .#* cvslog.* ,* CVS.adm .del-* *.a *.olb *.o *.obj\ *.so *.Z *~ *.old *.elc *.ln *.bak *.BAK *.orig *.rej *.exe _$* *$"; #define IGN_GROW 16 /* grow the list by 16 elements at a @@ -64,6 +65,9 @@ ign_setup () tmp = xstrdup (ign_default); ign_add (tmp, 0); free (tmp); + tmp = xstrdup(cvsDir); + ign_add (tmp, 0); + free (tmp); /* The client handles another way, by (after it does its own ignore file processing, and only if !ign_inhibit_server), letting the server @@ -428,8 +432,7 @@ ignore_files (ilist, entries, update_dir, proc) this directory if there is a CVS subdirectory. This will normally be the case, but the user may have messed up the working directory somehow. */ - p = xmalloc (strlen (file) + sizeof CVSADM + 10); - sprintf (p, "%s/%s", file, CVSADM); + xasprintf (&p, "%s/%s", file, CVSADM); dir = isdir (p); free (p); if (dir) @@ -462,8 +465,7 @@ ignore_files (ilist, entries, update_dir, proc) { char *temp; - temp = xmalloc (strlen (file) + sizeof (CVSADM) + 10); - (void) sprintf (temp, "%s/%s", file, CVSADM); + (void) xasprintf (&temp, "%s/%s", file, CVSADM); if (isdir (temp)) { free (temp); diff --git a/gnu/dist/xcvs/src/lock.c b/gnu/dist/xcvs/src/lock.c index 3f16f13e5fc9..cb07372a5db6 100644 --- a/gnu/dist/xcvs/src/lock.c +++ b/gnu/dist/xcvs/src/lock.c @@ -113,6 +113,7 @@ static void clear_lock PROTO ((struct lock *lock)); static void set_lockers_name PROTO((struct stat *statp)); static int set_writelock_proc PROTO((Node * p, void *closure)); static int unlock_proc PROTO((Node * p, void *closure)); +static int find_root PROTO((const char *repository, char *rootdir)); static int write_lock PROTO ((struct lock *lock)); static void lock_simple_remove PROTO ((struct lock *lock)); static void lock_wait PROTO((char *repository)); @@ -187,14 +188,19 @@ lock_name (repository, name) { struct stat sb; mode_t new_mode = 0; + int len; /* The interesting part of the repository is the part relative to CVSROOT. */ assert (current_parsed_root != NULL); assert (current_parsed_root->directory != NULL); - assert (strncmp (repository, current_parsed_root->directory, - strlen (current_parsed_root->directory)) == 0); - short_repos = repository + strlen (current_parsed_root->directory) + 1; + /* + * Unfortunately, string comparisons are not enough because we + * might have symlinks present + */ + len = find_root(repository, current_parsed_root->directory); + assert(len != -1); + short_repos = repository + len + 1; if (strcmp (repository, current_parsed_root->directory) == 0) short_repos = "."; @@ -297,6 +303,45 @@ lock_name (repository, name) return retval; } +/* + * Find the root directory in the repository directory + */ +static int +find_root(repository, rootdir) + const char *repository; + char *rootdir; +{ + struct stat strep, stroot; + char *p = NULL, *q = NULL; + size_t len; + + if (stat(rootdir, &stroot) == -1) + return -1; + len = strlen(repository); + do { + if (p != NULL) { + len = p - repository; + *p = '\0'; + } + if (q != NULL) + *q = '/'; + if (stat(repository, &strep) == -1) { + if (p != NULL) + *p = '/'; + return -1; + } + if (strep.st_dev == stroot.st_dev && strep.st_ino == stroot.st_ino) { + if (p != NULL) + *p = '/'; + if (q != NULL) + *q = '/'; + return len; + } + q = p; + } while ((p = strrchr(repository, '/')) != NULL); + return -1; +} + /* * Clean up all outstanding locks */ @@ -416,6 +461,9 @@ Reader_Lock (xrepository) FILE *fp; char *tmp; + if (nolock) + return (0); + if (trace) (void) fprintf (stderr, "%s-> Reader_Lock(%s)\n", CLIENT_SERVER_STR, xrepository); @@ -494,6 +542,8 @@ Writer_Lock (list) { char *wait_repos; + if (nolock) + return (0); if (noexec) return 0; diff --git a/gnu/dist/xcvs/src/logmsg.c b/gnu/dist/xcvs/src/logmsg.c index 55846d294181..f8eae4cb59eb 100644 --- a/gnu/dist/xcvs/src/logmsg.c +++ b/gnu/dist/xcvs/src/logmsg.c @@ -227,6 +227,8 @@ do_editor (dir, messagep, repository, changes) (*messagep)[strlen (*messagep) - 1] != '\n') (void) fprintf (fp, "\n"); } + else + (void) fprintf (fp, "\n"); if (repository != NULL) /* tack templates on if necessary */ @@ -298,7 +300,7 @@ do_editor (dir, messagep, repository, changes) run_setup (editinfo_editor ? editinfo_editor : Editor); run_arg (fname); if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, - RUN_NORMAL | RUN_SIGIGNORE)) != 0) + RUN_NORMAL | RUN_SIGIGNORE | RUN_UNSETXID)) != 0) error (editinfo_editor ? 1 : 0, retcode == -1 ? errno : 0, editinfo_editor ? "Logfile verification failed" : "warning: editor session failed"); @@ -679,6 +681,15 @@ title_proc (p, closure) strlen (str_list) + strlen (p->key) + 5); (void) strcat (str_list, p->key); break; + case 't': + str_list = + xrealloc (str_list, + (strlen (str_list) + + (li->tag ? strlen (li->tag) : 0) + + 10) + ); + (void) strcat (str_list, (li->tag ? li->tag : "")); + break; case 'V': str_list = xrealloc (str_list, diff --git a/gnu/dist/xcvs/src/main.c b/gnu/dist/xcvs/src/main.c index 40512b03f8d4..885d0f090a9b 100644 --- a/gnu/dist/xcvs/src/main.c +++ b/gnu/dist/xcvs/src/main.c @@ -46,6 +46,7 @@ int really_quiet = 0; int quiet = 0; int trace = 0; int noexec = 0; +int nolock = 0; int logoff = 0; /* Set if we should be writing CVSADM directories at top level. At @@ -54,6 +55,10 @@ int logoff = 0; int top_level_admin = 0; mode_t cvsumask = UMASK_DFLT; +char *RCS_citag = NULL; +char *CVS_admin_group = NULL; +char *CVS_admin_options = NULL; +const char *cvsDir = "CVS"; char *CurDir; @@ -243,11 +248,13 @@ static const char *const opt_usage[] = " -r Make checked-out files read-only.\n", " -w Make checked-out files read-write (default).\n", " -n Do not execute anything that will change the disk.\n", + " -u Don't create locks (implies -l).\n", " -t Show trace of program execution -- try with -n.\n", " -v CVS version and copyright.\n", " -T tmpdir Use 'tmpdir' for temporary files.\n", " -e editor Use 'editor' for editing log information.\n", " -d CVS_root Overrides $CVSROOT as the root of the CVS tree.\n", + " -D dir use DIR as the bookkeeping directory instead of CVS.\n" " -f Do not use the ~/.cvsrc file.\n", #ifdef CLIENT_SUPPORT " -z # Use compression level '#' for net traffic.\n", @@ -339,6 +346,10 @@ main_cleanup (sig) #ifndef DONT_USE_SIGNALS const char *name; char temp[10]; + static int reenter = 0; + + if (reenter++) + _exit(1); switch (sig) { @@ -401,7 +412,7 @@ main (argc, argv) int help = 0; /* Has the user asked for help? This lets us support the `cvs -H cmd' convention to give help for cmd. */ - static const char short_options[] = "+Qqrwtnvb:T:e:d:Hfz:s:xa"; + static const char short_options[] = "+Qqrwtnulvb:T:e:d:D:Hfz:s:xa"; static struct option long_options[] = { {"help", 0, NULL, 'H'}, @@ -525,6 +536,9 @@ main (argc, argv) break; case 'n': noexec = 1; + case 'u': /* Fall through */ + nolock = 1; + case 'l': /* Fall through */ logoff = 1; break; case 'v': @@ -613,6 +627,11 @@ distribution kit for a complete list of contributors and copyrights.\n", We will issue an error later if stream authentication is not supported. */ break; + case 'D': + cvsDir = xstrdup(optarg); + if (strchr(cvsDir, '/') != NULL) + error(1, 0, "cvsDir is not allowed to have slashes"); + break; case '?': default: usage (usg); @@ -887,7 +906,7 @@ distribution kit for a complete list of contributors and copyrights.\n", /* * Check to see if the repository exists. */ - if (!current_parsed_root->isremote) + if (!current_parsed_root->isremote && !nolock) { char *path; int save_errno; @@ -1121,6 +1140,26 @@ tm_to_internet (dest, source) source->tm_year + 1900, source->tm_hour, source->tm_min, source->tm_sec); } +const char * +getCVSDir(const char *suffix) +{ + static const char *buf[20][2]; + size_t i, len; + for (i = 0; i < 20; i++) + { + if (buf[i][0] == NULL) + break; + if (strcmp(buf[i][0], suffix) == 0) + return buf[i][1]; + } + if (i == 20) + error(1, 0, "Out of static buffer space"); + buf[i][0] = suffix; + buf[i][1] = xmalloc(len = strlen(cvsDir) + strlen(suffix) + 1); + snprintf((char *)buf[i][1], len, "%s%s", cvsDir, suffix); + return buf[i][1]; +} + void usage (cpp) register const char *const *cpp; diff --git a/gnu/dist/xcvs/src/mkmodules.c b/gnu/dist/xcvs/src/mkmodules.c index ba9d1f4a410d..e9ef5552b8dd 100644 --- a/gnu/dist/xcvs/src/mkmodules.c +++ b/gnu/dist/xcvs/src/mkmodules.c @@ -313,6 +313,9 @@ static const char *const config_contents[] = { "# repositories. Set it to `never' (the previous CVS behavior) to prevent\n", "# verifymsg scripts from changing the log message.\n", "#RereadLogAfterVerify=always\n", + "\n", + "# Set this to the name of a local tag to use in addition to Id\n", + "#tag=OurTag\n", NULL }; @@ -871,6 +874,10 @@ init (argc, argv) umask (cvsumask); + if (!admin_group_member()) + error (1, 0, "usage is restricted to members of the group %s", + CVS_admin_group); + if (argc == -1 || argc > 1) usage (init_usage); diff --git a/gnu/dist/xcvs/src/modules.c b/gnu/dist/xcvs/src/modules.c index 2643e1c8a455..314cbadeeeba 100644 --- a/gnu/dist/xcvs/src/modules.c +++ b/gnu/dist/xcvs/src/modules.c @@ -747,7 +747,8 @@ module `%s' is a request for a file in a module which is not a directory", cvs_output ("'\n", 0); cvs_flushout (); } - err += run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); + err += run_exec (RUN_TTY, RUN_TTY, RUN_TTY, + RUN_NORMAL | RUN_UNSETXID); free (expanded_path); } if (real_prog) free (real_prog); diff --git a/gnu/dist/xcvs/src/parseinfo.c b/gnu/dist/xcvs/src/parseinfo.c index 0fe9c33ed00f..95700a242282 100644 --- a/gnu/dist/xcvs/src/parseinfo.c +++ b/gnu/dist/xcvs/src/parseinfo.c @@ -357,6 +357,15 @@ parse_config (cvsroot) goto error_return; } } + else if (strcmp (line, "tag") == 0) { + RCS_citag = xstrdup(p); + } + else if (strcmp (line, "AdminGroup") == 0) { + CVS_admin_group = xstrdup(p); + } + else if (strcmp (line, "AdminOptions") == 0) { + CVS_admin_options = xstrdup(p); + } else if (strcmp (line, "PreservePermissions") == 0) { if (strcmp (p, "no") == 0) diff --git a/gnu/dist/xcvs/src/patch.c b/gnu/dist/xcvs/src/patch.c index 8490f96ccc2c..cd6e7ec07321 100644 --- a/gnu/dist/xcvs/src/patch.c +++ b/gnu/dist/xcvs/src/patch.c @@ -803,6 +803,11 @@ patch_dirproc (callerdat, dir, repos, update_dir, entries) static RETSIGTYPE patch_cleanup () { + static int reenter = 0; + + if (reenter++) + _exit(1); + /* Note that the checks for existence_error are because we are called from a signal handler, without SIG_begincrsect, so we don't know whether the files got created. */ diff --git a/gnu/dist/xcvs/src/rcs.c b/gnu/dist/xcvs/src/rcs.c index 72c6657885aa..404e6ff343d5 100644 --- a/gnu/dist/xcvs/src/rcs.c +++ b/gnu/dist/xcvs/src/rcs.c @@ -3534,7 +3534,7 @@ struct rcs_keyword size_t len; }; #define KEYWORD_INIT(s) (s), sizeof (s) - 1 -static const struct rcs_keyword keywords[] = +static struct rcs_keyword keywords[] = { { KEYWORD_INIT ("Author") }, { KEYWORD_INIT ("Date") }, @@ -3547,6 +3547,7 @@ static const struct rcs_keyword keywords[] = { KEYWORD_INIT ("Revision") }, { KEYWORD_INIT ("Source") }, { KEYWORD_INIT ("State") }, + { NULL, 0 }, { NULL, 0 } }; enum keyword @@ -3561,7 +3562,8 @@ enum keyword KEYWORD_RCSFILE, KEYWORD_REVISION, KEYWORD_SOURCE, - KEYWORD_STATE + KEYWORD_STATE, + KEYWORD_LOCALID }; /* Convert an RCS date string into a readable string. This is like @@ -3698,6 +3700,11 @@ expand_keywords (rcs, ver, name, log, loglen, expand, buf, len, retbuf, retlen) return; } + if (RCS_citag != NULL) { + keywords[KEYWORD_LOCALID].string = RCS_citag; + keywords[KEYWORD_LOCALID].len = strlen(RCS_citag); + } + /* If we are using -kkvl, dig out the locker information if any. */ locker = NULL; if (expand == KFLAG_KVL) @@ -3789,6 +3796,7 @@ expand_keywords (rcs, ver, name, log, loglen, expand, buf, len, retbuf, retlen) case KEYWORD_HEADER: case KEYWORD_ID: + case KEYWORD_LOCALID: { const char *path; int free_path; @@ -4462,7 +4470,7 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) if (info != NULL) { /* If the size of `devtype' changes, fix the sscanf call also */ - char devtype[16]; + char devtype[16+1]; if (sscanf (info->data, "%15s %lu", devtype, &devnum_long) < 2) @@ -8464,6 +8472,10 @@ count_delta_actions (np, ignore) RETSIGTYPE rcs_cleanup () { + static int reenter = 0; + + if (reenter++) + _exit(1); /* Note that the checks for existence_error are because we are called from a signal handler, so we don't know whether the files got created. */ diff --git a/gnu/dist/xcvs/src/recurse.c b/gnu/dist/xcvs/src/recurse.c index 2416861d96af..49a138106acc 100644 --- a/gnu/dist/xcvs/src/recurse.c +++ b/gnu/dist/xcvs/src/recurse.c @@ -551,7 +551,7 @@ do_recursion (frame) if (frame->flags == R_SKIP_ALL) return (0); - locktype = noexec ? CVS_LOCK_NONE : frame->locktype; + locktype = nolock ? CVS_LOCK_NONE : frame->locktype; /* The fact that locks are not active here is what makes us fail to have the @@ -1009,8 +1009,8 @@ but CVS uses %s for its own purposes; skipping %s directory", char *cvsadmdir; cvsadmdir = xmalloc (strlen (dir) - + sizeof (CVSADM_REP) - + sizeof (CVSADM_ENT) + + strlen (CVSADM_REP) + + strlen (CVSADM_ENT) + 80); strcpy (cvsadmdir, dir); diff --git a/gnu/dist/xcvs/src/remove.c b/gnu/dist/xcvs/src/remove.c index a09cfd475493..3f2fabe62813 100644 --- a/gnu/dist/xcvs/src/remove.c +++ b/gnu/dist/xcvs/src/remove.c @@ -204,11 +204,7 @@ remove_fileproc (callerdat, finfo) * remove the ,t file for it and scratch it from the * entries file. */ Scratch_Entry (finfo->entries, finfo->file); - fname = xmalloc (strlen (finfo->file) - + sizeof (CVSADM) - + sizeof (CVSEXT_LOG) - + 10); - (void) sprintf (fname, "%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG); + (void) xasprintf (&fname, "%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG); if (unlink_file (fname) < 0 && !existence_error (errno)) error (0, errno, "cannot remove %s", CVSEXT_LOG); diff --git a/gnu/dist/xcvs/src/repos.c b/gnu/dist/xcvs/src/repos.c index 202d92d5e8a2..18c5c89b101d 100644 --- a/gnu/dist/xcvs/src/repos.c +++ b/gnu/dist/xcvs/src/repos.c @@ -42,10 +42,7 @@ Name_Repository (dir, update_dir) xupdate_dir = "."; if (dir != NULL) - { - tmp = xmalloc (strlen (dir) + sizeof (CVSADM_REP) + 10); - (void) sprintf (tmp, "%s/%s", dir, CVSADM_REP); - } + (void) xasprintf (&tmp, "%s/%s", dir, CVSADM_REP); else tmp = xstrdup (CVSADM_REP); @@ -61,10 +58,7 @@ Name_Repository (dir, update_dir) char *cvsadm; if (dir != NULL) - { - cvsadm = xmalloc (strlen (dir) + sizeof (CVSADM) + 10); - (void) sprintf (cvsadm, "%s/%s", dir, CVSADM); - } + (void) xasprintf (&cvsadm, "%s/%s", dir, CVSADM); else cvsadm = xstrdup (CVSADM); diff --git a/gnu/dist/xcvs/src/root.c b/gnu/dist/xcvs/src/root.c index 56ba1cd9cb8c..eb749d4d8505 100644 --- a/gnu/dist/xcvs/src/root.c +++ b/gnu/dist/xcvs/src/root.c @@ -50,10 +50,8 @@ Name_Root (dir, update_dir) if (dir != NULL) { - cvsadm = xmalloc (strlen (dir) + sizeof (CVSADM) + 10); - (void) sprintf (cvsadm, "%s/%s", dir, CVSADM); - tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); - (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); + (void) xasprintf (&cvsadm, "%s/%s", dir, CVSADM); + (void) xasprintf (&tmp, "%s/%s", dir, CVSADM_ROOT); } else { @@ -155,10 +153,7 @@ Create_Root (dir, rootdir) if (rootdir != NULL) { if (dir != NULL) - { - tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); - (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); - } + (void) xasprintf (&tmp, "%s/%s", dir, CVSADM_ROOT); else tmp = xstrdup (CVSADM_ROOT); diff --git a/gnu/dist/xcvs/src/run.c b/gnu/dist/xcvs/src/run.c index b686681e69e6..512e68c41fc9 100644 --- a/gnu/dist/xcvs/src/run.c +++ b/gnu/dist/xcvs/src/run.c @@ -208,6 +208,13 @@ run_exec (stin, stout, sterr, flags) #endif if (pid == 0) { +#ifdef SETXID_SUPPORT + if (flags & RUN_UNSETXID) { + (void) setgid (getgid ()); + (void) setuid (getuid ()); + } +#endif + if (shin != 0) { (void) dup2 (shin, 0); diff --git a/gnu/dist/xcvs/src/server.c b/gnu/dist/xcvs/src/server.c index 627fc0c72dc8..079ef62d6b9f 100644 --- a/gnu/dist/xcvs/src/server.c +++ b/gnu/dist/xcvs/src/server.c @@ -782,6 +782,7 @@ E Protocol error: Root says \"%s\" but pserver says \"%s\"", nothing. But for rsh, we need to do it now. */ parse_config (current_parsed_root->directory); + if (!nolock) { path = xmalloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM) + 2); @@ -799,6 +800,7 @@ E Protocol error: Root says \"%s\" but pserver says \"%s\"", pending_error = save_errno; } free (path); + } #ifdef HAVE_PUTENV env = xmalloc (strlen (CVSROOT_ENV) + strlen (current_parsed_root->directory) + 2); @@ -2285,6 +2287,9 @@ serve_global_option (arg) noexec = 1; logoff = 1; break; + case 'u': + nolock = 1; + break; case 'q': quiet = 1; break; @@ -4796,6 +4801,8 @@ struct request requests[] = REQ_LINE("Max-dotdot", serve_max_dotdot, 0), REQ_LINE("Static-directory", serve_static_directory, 0), REQ_LINE("Sticky", serve_sticky, 0), + REQ_LINE("Checkin-prog", serve_noop, 0), + REQ_LINE("Update-prog", serve_noop, 0), REQ_LINE("Entry", serve_entry, RQ_ESSENTIAL), REQ_LINE("Kopt", serve_kopt, 0), REQ_LINE("Checkin-time", serve_checkin_time, 0), @@ -5306,6 +5313,7 @@ switch_to_user (cvs_username, username) const char *username; { struct passwd *pw; + int rc; pw = getpwnam (username); if (pw == NULL) @@ -5384,7 +5392,14 @@ error 0 %s: no such system user\n", username); } } - if (setuid (pw->pw_uid) < 0) +#ifdef SETXID_SUPPORT + /* Honor the setuid bit iff set. */ + if (getuid() != geteuid()) + rc = setuid (geteuid ()); + else +#endif + rc = setuid (pw->pw_uid); + if (rc < 0) { /* Note that this means that if run as a non-root user, CVSROOT/passwd must contain the user we are running as @@ -5953,19 +5968,20 @@ kserver_authenticate_connection () { int status; char instance[INST_SZ]; - struct sockaddr_in peer; - struct sockaddr_in laddr; - int len; + struct sockaddr_storage peer; + struct sockaddr_storage laddr; + int plen, llen; KTEXT_ST ticket; AUTH_DAT auth; char version[KRB_SENDAUTH_VLEN]; char user[ANAME_SZ]; strcpy (instance, "*"); - len = sizeof peer; - if (getpeername (STDIN_FILENO, (struct sockaddr *) &peer, &len) < 0 + plen = sizeof peer; + llen = sizeof laddr; + if (getpeername (STDIN_FILENO, (struct sockaddr *) &peer, &plen) < 0 || getsockname (STDIN_FILENO, (struct sockaddr *) &laddr, - &len) < 0) + &llen) < 0) { printf ("E Fatal error, aborting.\n\ error %s getpeername or getsockname failed\n", strerror (errno)); @@ -5990,7 +6006,8 @@ error %s getpeername or getsockname failed\n", strerror (errno)); #endif status = krb_recvauth (KOPT_DO_MUTUAL, STDIN_FILENO, &ticket, "rcmd", - instance, &peer, &laddr, &auth, "", sched, + instance, (struct sockaddr_in *)&peer, + (struct sockaddr_in *)&laddr, &auth, "", sched, version); if (status != KSUCCESS) { @@ -6342,7 +6359,7 @@ krb_encrypt_input (fnclosure, input, output, size) struct krb_encrypt_data *kd = (struct krb_encrypt_data *) fnclosure; int tcount; - des_cbc_encrypt ((C_Block *) input, (C_Block *) output, + des_cbc_encrypt ((char *) input, (char *) output, size, kd->sched, &kd->block, 0); /* SIZE is the size of the buffer, which is set by the encryption @@ -6389,7 +6406,7 @@ krb_encrypt_output (fnclosure, input, output, size, translated) fail over a long network connection. We trust krb_recvauth to guard against a replay attack. */ - des_cbc_encrypt ((C_Block *) input, (C_Block *) output, aligned, + des_cbc_encrypt ((char *) input, (char *) output, aligned, kd->sched, &kd->block, 1); *translated = aligned; diff --git a/gnu/dist/xcvs/src/subr.c b/gnu/dist/xcvs/src/subr.c index faa988a82626..83546a13bbfc 100644 --- a/gnu/dist/xcvs/src/subr.c +++ b/gnu/dist/xcvs/src/subr.c @@ -16,6 +16,7 @@ #include #include "cvs.h" #include "getline.h" +#include #ifdef HAVE_NANOSLEEP # include "xtime.h" @@ -81,6 +82,21 @@ xrealloc (ptr, bytes) return (cp); } +int +xasprintf(char **buf, const char *fmt, ...) +{ + int len; + va_list ap; + + va_start(ap, fmt); + len = vasprintf(buf, fmt, ap); + va_end(ap); + + if (len == -1) + error(1, 0, "out of memory: xasprintf(..., \"%s\", ...) failed", fmt); + return len; +} + /* Two constants which tune expand_string. Having MIN_INCR as large as 1024 might waste a bit of memory, but it shouldn't be too bad (CVS used to allocate arrays of, say, 3000, PATH_MAX (8192, often), diff --git a/gnu/dist/xcvs/src/tag.c b/gnu/dist/xcvs/src/tag.c index fcc1c74f4182..ef9e57473b31 100644 --- a/gnu/dist/xcvs/src/tag.c +++ b/gnu/dist/xcvs/src/tag.c @@ -1411,8 +1411,6 @@ Numeric tag %s contains characters other than digits and '.'", name); add_to_val_tags (name); } - - /* * Check whether a join tag is valid. This is just like * tag_check_valid, but we must stop before the colon if there is one. diff --git a/gnu/dist/xcvs/src/update.c b/gnu/dist/xcvs/src/update.c index 0cae14f693c9..085aedaca298 100644 --- a/gnu/dist/xcvs/src/update.c +++ b/gnu/dist/xcvs/src/update.c @@ -977,8 +977,7 @@ update_dirent_proc (callerdat, dir, repository, update_dir, entries) { char *tmp; - tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ENTSTAT) + 10); - (void) sprintf (tmp, "%s/%s", dir, CVSADM_ENTSTAT); + (void) xasprintf (&tmp, "%s/%s", dir, CVSADM_ENTSTAT); if (unlink_file (tmp) < 0 && ! existence_error (errno)) error (1, errno, "cannot remove file %s", tmp); #ifdef SERVER_SUPPORT @@ -1234,11 +1233,7 @@ checkout_file (finfo, vers_ts, adding, merging, update_server) we are the server. */ if (!pipeout && !server_active) { - backup = xmalloc (strlen (finfo->file) - + sizeof (CVSADM) - + sizeof (CVSPREFIX) - + 10); - (void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, finfo->file); + (void) xasprintf (&backup, "%s/%s%s", CVSADM, CVSPREFIX, finfo->file); if (isfile (finfo->file)) rename_file (finfo->file, backup); else @@ -1341,11 +1336,18 @@ VERS: ", 0); xchmod (finfo->file, 1); else { + mode_t oumask, writeaccess; + /* We know that we are the server here, so although xchmod checks umask, we don't bother. */ - mode |= (((mode & S_IRUSR) ? S_IWUSR : 0) + /* Not bothering with the umask makes the files + mode 0777 on old clients, though. -chb */ + oumask = umask(0); + (void) umask(oumask); + writeaccess = (((mode & S_IRUSR) ? S_IWUSR : 0) | ((mode & S_IRGRP) ? S_IWGRP : 0) | ((mode & S_IROTH) ? S_IWOTH : 0)); + mode |= (~oumask) & writeaccess; } } @@ -1605,11 +1607,7 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum) return 0; } - backup = xmalloc (strlen (finfo->file) - + sizeof (CVSADM) - + sizeof (CVSPREFIX) - + 10); - (void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, finfo->file); + (void) xasprintf (&backup, "%s/%s%s", CVSADM, CVSPREFIX, finfo->file); if (isfile (finfo->file)) rename_file (finfo->file, backup); else @@ -1619,16 +1617,8 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum) error (0, errno, "cannot remove %s", backup); } - file1 = xmalloc (strlen (finfo->file) - + sizeof (CVSADM) - + sizeof (CVSPREFIX) - + 10); - (void) sprintf (file1, "%s/%s%s-1", CVSADM, CVSPREFIX, finfo->file); - file2 = xmalloc (strlen (finfo->file) - + sizeof (CVSADM) - + sizeof (CVSPREFIX) - + 10); - (void) sprintf (file2, "%s/%s%s-2", CVSADM, CVSPREFIX, finfo->file); + (void) xasprintf (&file1, "%s/%s%s-1", CVSADM, CVSPREFIX, finfo->file); + (void) xasprintf (&file2, "%s/%s%s-2", CVSADM, CVSPREFIX, finfo->file); fail = 0;