diff -ur cvs-orig/src/logmsg.c cvs-1.11.2/src/logmsg.c --- cvs-orig/src/logmsg.c 2001-09-14 13:12:10.000000000 -0400 +++ cvs-1.11.2/src/logmsg.c 2002-09-19 16:30:01.134153000 -0400 @@ -928,9 +928,10 @@ fprintf (pipefp, "%s\n\n", cp); free (cp); } + if (CVS_Username) (void) fprintf (pipefp, "By user %s\n", CVS_Username); setup_tmpfile (pipefp, "", changes); - (void) fprintf (pipefp, "Log Message:\n%s\n", message); + (void) fprintf (pipefp, "\nLog Message:\n\n%s\n", message); if (logfp != (FILE *) 0) { (void) fprintf (pipefp, "Status:\n"); diff -ur cvs-orig/src/main.c cvs-1.11.2/src/main.c --- cvs-orig/src/main.c 2002-03-19 14:15:45.000000000 -0500 +++ cvs-1.11.2/src/main.c 2002-06-24 18:07:37.557244000 -0400 @@ -21,6 +21,10 @@ extern int gethostname (); #endif +#ifdef HAVE_GSSAPI +extern char *krb5_overridekeyname; +#endif + char *program_name; char *program_path; char *command_name; @@ -263,6 +267,9 @@ " -a Authenticate all net traffic.\n", #endif " -s VAR=VAL Set CVS user variable.\n", +#ifdef HAVE_GSSAPI + " -k keytab Specify path to the Kerberos keytab.\n", +#endif "(Specify the --help option for a list of other help options)\n", NULL }; @@ -402,12 +409,13 @@ int tmpdir_update_env, cvs_update_env; int free_CVSroot = 0; int free_Editor = 0; + int free_Keytab = 0; int free_Tmpdir = 0; 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[] = "+Qqrwtnlvb:T:e:d:Hfz:s:xa"; + static const char short_options[] = "+Qqrwtnlvb:T:e:d:Hfz:s:xak:"; static struct option long_options[] = { {"help", 0, NULL, 'H'}, @@ -620,6 +628,12 @@ We will issue an error later if stream authentication is not supported. */ break; + case 'k': +#ifdef HAVE_GSSAPI + krb5_overridekeyname = xstrdup (optarg); + free_Keytab = 1; +#endif + break; case '?': default: usage (usg); @@ -1022,6 +1036,8 @@ free (CVSroot); if (free_Editor) free (Editor); + if (free_Keytab) + free (krb5_overridekeyname); if (free_Tmpdir) free (Tmpdir); root_allow_free (); diff -ur cvs-orig/src/server.c cvs-1.11.2/src/server.c --- cvs-orig/src/server.c 2002-03-19 14:15:45.000000000 -0500 +++ cvs-1.11.2/src/server.c 2002-09-17 12:35:06.706721000 -0400 @@ -104,10 +104,9 @@ # include # endif -/* The cvs username sent by the client, which might or might not be - the same as the system username the server eventually switches to - run as. CVS_Username gets set iff password authentication is - successful. */ +/* The cvs username sent by the client, which may not be the same as + the username under which the server runs. CVS_Username gets set + iff authentication is successful. */ char *CVS_Username = NULL; /* Used to check that same repos is transmitted in pserver auth and in @@ -2429,176 +2428,109 @@ } +/* Determine whether the user is listed in a file of usernames. + */ +static int +check_user_in_file_p (fname) + char *fname; +{ + FILE *fp; + char *linebuf = NULL; + size_t linebuf_len = 0; + int num_red = 0; + + fp = fopen (fname, "r"); + if (fp == NULL) + { + error (0, errno, "cannot open %s", fname); + return 0; + } + + while ((num_red = getline (&linebuf, &linebuf_len, fp)) > 0) + { + /* Chop newline by hand, for strcmp()'s sake. */ + if (linebuf[num_red - 1] == '\n') + linebuf[num_red - 1] = '\0'; + if (strcmp (linebuf, CVS_Username) == 0) + break; /* success - we will return 1 */ + } + + if (num_red < 0 && !feof (fp)) + error (0, errno, "error while reading %s", fname); + if (linebuf) + free(linebuf); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", fname); + return num_red > 0 ? 1 : 0; +} /* If command is legal, return 1. - * Else if command is illegal and croak_on_illegal is set, then die. - * Else just return 0 to indicate that command is illegal. + * Else return 0 to indicate that command is illegal. */ static int check_command_legal_p (cmd_name) char *cmd_name; { - /* Right now, only pserver notices illegal commands -- namely, - * write attempts by a read-only user. Therefore, if CVS_Username - * is not set, this just returns 1, because CVS_Username unset - * means pserver is not active. - */ #ifdef AUTH_SERVER_SUPPORT + char *fname; + size_t flen; + int found_it = 0; + + /* If CVS_Username is not set, this just returns 1, because an + * unset CVS_Username means that system file permissions are being + * used as the permissions mechanism. + */ + if (CVS_Username == NULL) return 1; - + + /* If the user is listed in the writers file, then any command is + * allowed. */ + + flen = strlen (current_parsed_root->directory) + + strlen (CVSROOTADM) + + strlen (CVSROOTADM_WRITERS) + + 3; + + fname = xmalloc (flen); + (void) sprintf (fname, "%s/%s/%s", current_parsed_root->directory, + CVSROOTADM, CVSROOTADM_WRITERS); + found_it = check_user_in_file_p (fname); + free(fname); + + if (found_it) + return 1; + + /* If the operation *requires* write access, the writers file was + * the only chance for permission. */ + if (lookup_command_attribute (cmd_name) & CVS_CMD_MODIFIES_REPOSITORY) - { - /* This command has the potential to modify the repository, so - * we check if the user have permission to do that. - * - * (Only relevant for remote users -- local users can do - * whatever normal Unix file permissions allow them to do.) - * - * The decision method: - * - * If $CVSROOT/CVSADMROOT_READERS exists and user is listed - * in it, then read-only access for user. - * - * Or if $CVSROOT/CVSADMROOT_WRITERS exists and user NOT - * listed in it, then also read-only access for user. - * - * Else read-write access for user. - */ + return 0; + + /* If the command does not require write access, we can check the + * readers file. */ + + flen = strlen (current_parsed_root->directory) + + strlen (CVSROOTADM) + + strlen (CVSROOTADM_READERS) + + 3; + + fname = xmalloc (flen); + (void) sprintf (fname, "%s/%s/%s", current_parsed_root->directory, + CVSROOTADM, CVSROOTADM_READERS); + + found_it = check_user_in_file_p (fname); + free(fname); + + if (found_it) + return 1; - char *linebuf = NULL; - int num_red = 0; - size_t linebuf_len = 0; - char *fname; - size_t flen; - FILE *fp; - int found_it = 0; - - /* else */ - flen = strlen (current_parsed_root->directory) - + strlen (CVSROOTADM) - + strlen (CVSROOTADM_READERS) - + 3; - - fname = xmalloc (flen); - (void) sprintf (fname, "%s/%s/%s", current_parsed_root->directory, - CVSROOTADM, CVSROOTADM_READERS); - - fp = fopen (fname, "r"); - - if (fp == NULL) - { - if (!existence_error (errno)) - { - /* Need to deny access, so that attackers can't fool - us with some sort of denial of service attack. */ - error (0, errno, "cannot open %s", fname); - free (fname); - return 0; - } - } - else /* successfully opened readers file */ - { - while ((num_red = getline (&linebuf, &linebuf_len, fp)) >= 0) - { - /* Hmmm, is it worth importing my own readline - library into CVS? It takes care of chopping - leading and trailing whitespace, "#" comments, and - newlines automatically when so requested. Would - save some code here... -kff */ - - /* Chop newline by hand, for strcmp()'s sake. */ - if (linebuf[num_red - 1] == '\n') - linebuf[num_red - 1] = '\0'; - - if (strcmp (linebuf, CVS_Username) == 0) - goto handle_illegal; - } - if (num_red < 0 && !feof (fp)) - error (0, errno, "cannot read %s", fname); - - /* If not listed specifically as a reader, then this user - has write access by default unless writers are also - specified in a file . */ - if (fclose (fp) < 0) - error (0, errno, "cannot close %s", fname); - } - free (fname); - - /* Now check the writers file. */ - - flen = strlen (current_parsed_root->directory) - + strlen (CVSROOTADM) - + strlen (CVSROOTADM_WRITERS) - + 3; - - fname = xmalloc (flen); - (void) sprintf (fname, "%s/%s/%s", current_parsed_root->directory, - CVSROOTADM, CVSROOTADM_WRITERS); - - fp = fopen (fname, "r"); - - if (fp == NULL) - { - if (linebuf) - free (linebuf); - if (existence_error (errno)) - { - /* Writers file does not exist, so everyone is a writer, - by default. */ - free (fname); - return 1; - } - else - { - /* Need to deny access, so that attackers can't fool - us with some sort of denial of service attack. */ - error (0, errno, "cannot read %s", fname); - free (fname); - return 0; - } - } - - found_it = 0; - while ((num_red = getline (&linebuf, &linebuf_len, fp)) >= 0) - { - /* Chop newline by hand, for strcmp()'s sake. */ - if (linebuf[num_red - 1] == '\n') - linebuf[num_red - 1] = '\0'; - - if (strcmp (linebuf, CVS_Username) == 0) - { - found_it = 1; - break; - } - } - if (num_red < 0 && !feof (fp)) - error (0, errno, "cannot read %s", fname); - - if (found_it) - { - if (fclose (fp) < 0) - error (0, errno, "cannot close %s", fname); - if (linebuf) - free (linebuf); - free (fname); - return 1; - } - else /* writers file exists, but this user not listed in it */ - { - handle_illegal: - if (fclose (fp) < 0) - error (0, errno, "cannot close %s", fname); - if (linebuf) - free (linebuf); - free (fname); - return 0; - } - } -#endif /* AUTH_SERVER_SUPPORT */ + /* If the user is listed in neither file, we forbid.*/ + return 0; - /* If ever reach end of this function, command must be legal. */ +#else /* AUTH_SERVER_SUPPORT */ return 1; +#endif /* AUTH_SERVER_SUPPORT */ } @@ -2660,8 +2592,12 @@ buf_output0 (buf_to_net, program_name); buf_output0 (buf_to_net, " [server aborted]: \""); buf_output0 (buf_to_net, cmd_name); - buf_output0 (buf_to_net, "\" requires write access to the repository\n\ -error \n"); + buf_output0 (buf_to_net, "\" requires "); + if (lookup_command_attribute (cmd_name) & CVS_CMD_MODIFIES_REPOSITORY) + buf_output0 (buf_to_net, "write"); + else + buf_output0 (buf_to_net, "read"); + buf_output0 (buf_to_net, " access to the repository\nerror \n"); goto free_args_and_return; } @@ -5279,6 +5215,31 @@ static void switch_to_user PROTO((const char *)); static void +set_user_env (username) +const char *username; +{ +#if HAVE_PUTENV + /* Set LOGNAME, USER and CVS_USER in the environment, in case they + are already set to something else. */ + char *env; + + env = xmalloc (sizeof "LOGNAME=" + strlen (username)); + (void) sprintf (env, "LOGNAME=%s", username); + (void) putenv (env); + + env = xmalloc (sizeof "USER=" + strlen (username)); + (void) sprintf (env, "USER=%s", username); + (void) putenv (env); + +#ifdef AUTH_SERVER_SUPPORT + env = xmalloc (sizeof "CVS_USER=" + strlen (CVS_Username)); + (void) sprintf (env, "CVS_USER=%s", CVS_Username); + (void) putenv (env); +#endif +#endif /* HAVE_PUTENV */ +} + +static void switch_to_user (username) const char *username; { @@ -5366,27 +5327,7 @@ CVS_Username = xstrdup (username); #endif -#if HAVE_PUTENV - /* Set LOGNAME, USER and CVS_USER in the environment, in case they - are already set to something else. */ - { - char *env; - - env = xmalloc (sizeof "LOGNAME=" + strlen (username)); - (void) sprintf (env, "LOGNAME=%s", username); - (void) putenv (env); - - env = xmalloc (sizeof "USER=" + strlen (username)); - (void) sprintf (env, "USER=%s", username); - (void) putenv (env); - -#ifdef AUTH_SERVER_SUPPORT - env = xmalloc (sizeof "CVS_USER=" + strlen (CVS_Username)); - (void) sprintf (env, "CVS_USER=%s", CVS_Username); - (void) putenv (env); -#endif - } -#endif /* HAVE_PUTENV */ + set_user_env(username); } #endif @@ -6058,8 +5999,7 @@ if (gss_display_name (&stat_min, client_name, &desc, &mechid) != GSS_S_COMPLETE || krb5_parse_name (kc, ((gss_buffer_t) &desc)->value, &p) != 0 - || krb5_aname_to_localname (kc, p, sizeof buf, buf) != 0 - || krb5_kuserok (kc, p, buf) != TRUE) + || krb5_aname_to_localname (kc, p, sizeof buf, buf) != 0) { error (1, 0, "access denied"); } @@ -6079,7 +6019,9 @@ error (1, errno, "fwrite failed"); } - switch_to_user (buf); + CVS_Username = xmalloc (strlen (buf) + 1); + strcpy (CVS_Username, buf); + set_user_env(buf); /* set LOGNAME, USER, CVS_USER */ printf ("I LOVE YOU\n"); fflush (stdout);