xref: /openbsd/gnu/usr.bin/cvs/src/login.c (revision f9bbbf45)
113571821Stholo /*
213571821Stholo  * Copyright (c) 1995, Cyclic Software, Bloomington, IN, USA
313571821Stholo  *
413571821Stholo  * You may distribute under the terms of the GNU General Public License as
513571821Stholo  * specified in the README file that comes with CVS.
613571821Stholo  *
713571821Stholo  * Allow user to log in for an authenticating server.
813571821Stholo  */
913571821Stholo 
1013571821Stholo #include "cvs.h"
11c26070a5Stholo #include "getline.h"
1213571821Stholo 
1313571821Stholo #ifdef AUTH_CLIENT_SUPPORT   /* This covers the rest of the file. */
1413571821Stholo 
15e77048c1Stholo #ifdef HAVE_GETPASSPHRASE
16e77048c1Stholo #define GETPASS getpassphrase
17e77048c1Stholo #else
18e77048c1Stholo #define GETPASS getpass
19e77048c1Stholo #endif
20e77048c1Stholo 
21780d15dfStholo /* There seems to be very little agreement on which system header
22780d15dfStholo    getpass is declared in.  With a lot of fancy autoconfiscation,
23780d15dfStholo    we could perhaps detect this, but for now we'll just rely on
24780d15dfStholo    _CRAY, since Cray is perhaps the only system on which our own
25780d15dfStholo    declaration won't work (some Crays declare the 2#$@% thing as
26780d15dfStholo    varadic, believe it or not).  On Cray, getpass will be declared
27780d15dfStholo    in either stdlib.h or unistd.h.  */
28780d15dfStholo #ifndef _CRAY
29e77048c1Stholo extern char *GETPASS ();
30780d15dfStholo #endif
3113571821Stholo 
3213571821Stholo #ifndef CVS_PASSWORD_FILE
3313571821Stholo #define CVS_PASSWORD_FILE ".cvspass"
3413571821Stholo #endif
3513571821Stholo 
3613571821Stholo /* If non-NULL, get_cvs_password() will just return this. */
3713571821Stholo static char *cvs_password = NULL;
3813571821Stholo 
39461cc63eStholo static char *construct_cvspass_filename PROTO ((void));
40461cc63eStholo 
4113571821Stholo /* The return value will need to be freed. */
42461cc63eStholo static char *
construct_cvspass_filename()4313571821Stholo construct_cvspass_filename ()
4413571821Stholo {
4513571821Stholo     char *homedir;
4613571821Stholo     char *passfile;
4713571821Stholo 
4813571821Stholo     /* Environment should override file. */
4913571821Stholo     if ((passfile = getenv ("CVS_PASSFILE")) != NULL)
5013571821Stholo 	return xstrdup (passfile);
5113571821Stholo 
5213571821Stholo     /* Construct absolute pathname to user's password file. */
53c26070a5Stholo     /* todo: does this work under OS/2 ? */
54c26070a5Stholo     homedir = get_homedir ();
5513571821Stholo     if (! homedir)
5613571821Stholo     {
57c71bc7e2Stholo 	/* FIXME?  This message confuses a lot of users, at least
58c71bc7e2Stholo 	   on Win95 (which doesn't set HOMEDRIVE and HOMEPATH like
59c71bc7e2Stholo 	   NT does).  I suppose the answer for Win95 is to store the
60c71bc7e2Stholo 	   passwords in the registry or something (??).  And .cvsrc
61c71bc7e2Stholo 	   and such too?  Wonder what WinCVS does (about .cvsrc, the
62c71bc7e2Stholo 	   right thing for a GUI is to just store the password in
63c71bc7e2Stholo 	   memory only)...  */
64c71bc7e2Stholo 	error (1, 0, "could not find out home directory");
6513571821Stholo 	return (char *) NULL;
6613571821Stholo     }
6713571821Stholo 
6813571821Stholo     passfile =
6913571821Stholo 	(char *) xmalloc (strlen (homedir) + strlen (CVS_PASSWORD_FILE) + 3);
7013571821Stholo     strcpy (passfile, homedir);
7150bf276cStholo #ifndef NO_SLASH_AFTER_HOME
7250bf276cStholo     /* NO_SLASH_AFTER_HOME is defined for VMS, where foo:[bar]/.cvspass is not
7350bf276cStholo        a legal filename but foo:[bar].cvspass is.  A more clean solution would
7450bf276cStholo        be something more along the lines of a "join a directory to a filename"
7550bf276cStholo        kind of thing....  */
7613571821Stholo     strcat (passfile, "/");
7750bf276cStholo #endif
7813571821Stholo     strcat (passfile, CVS_PASSWORD_FILE);
7913571821Stholo 
8013571821Stholo     /* Safety first and last, Scouts. */
8113571821Stholo     if (isfile (passfile))
8213571821Stholo 	/* xchmod() is too polite. */
8313571821Stholo 	chmod (passfile, 0600);
8413571821Stholo 
8513571821Stholo     return passfile;
8613571821Stholo }
8713571821Stholo 
8813571821Stholo 
8943c1707eStholo 
9043c1707eStholo /*
9143c1707eStholo  * static char *
9243c1707eStholo  * password_entry_parseline (
9343c1707eStholo  *			      const char *cvsroot_canonical,
9443c1707eStholo  *			      const unsigned char warn,
9543c1707eStholo  *			      const int linenumber,
9643c1707eStholo  *			      char *linebuf
9743c1707eStholo  *			     );
9843c1707eStholo  *
9943c1707eStholo  * Internal function used by password_entry_operation.  Parse a single line
10043c1707eStholo  * from a ~/.cvsroot password file and return a pointer to the password if the
10143c1707eStholo  * line refers to the same cvsroot as cvsroot_canonical
10243c1707eStholo  *
10343c1707eStholo  * INPUTS
10443c1707eStholo  *	cvsroot_canonical	the root we are looking for
10543c1707eStholo  *	warn			Boolean: print warnings for invalid lines?
10643c1707eStholo  *	linenumber		the line number for error messages
10743c1707eStholo  *	linebuf			the current line
10843c1707eStholo  *
10943c1707eStholo  * RETURNS
11043c1707eStholo  * 	NULL			if the line doesn't match
11143c1707eStholo  * 	char *password		as a pointer into linebuf
11243c1707eStholo  *
11343c1707eStholo  * NOTES
11443c1707eStholo  *	This function temporarily alters linebuf, so it isn't thread safe when
11543c1707eStholo  *	called on the same linebuf
11643c1707eStholo  */
11743c1707eStholo static char *
password_entry_parseline(cvsroot_canonical,warn,linenumber,linebuf)11843c1707eStholo password_entry_parseline (cvsroot_canonical, warn, linenumber, linebuf)
11943c1707eStholo     const char *cvsroot_canonical;
12043c1707eStholo     const unsigned char warn;
12143c1707eStholo     const int linenumber;
12243c1707eStholo     char *linebuf;
12343c1707eStholo {
12443c1707eStholo     char *password = NULL;
12543c1707eStholo     char *p;
12643c1707eStholo 
12743c1707eStholo     /* look for '^/' */
12843c1707eStholo     if (*linebuf == '/')
12943c1707eStholo     {
13043c1707eStholo 	/* Yes: slurp '^/\d+\D' and parse the rest of the line according to version number */
13143c1707eStholo 	char *q;
13243c1707eStholo 	unsigned long int entry_version;
13343c1707eStholo 
13443c1707eStholo 	if (isspace(*(linebuf + 1)))
13543c1707eStholo 	    /* special case since strtoul ignores leading white space */
136420bf761Sotto 	    q = linebuf + 1;
13743c1707eStholo 	else
13843c1707eStholo 	    entry_version = strtoul (linebuf + 1, &q, 10);
13943c1707eStholo 
14043c1707eStholo 	if (q == linebuf + 1)
14143c1707eStholo 	    /* no valid digits found by strtoul */
14243c1707eStholo 	    entry_version = 0;
14343c1707eStholo 	else
14443c1707eStholo 	    /* assume a delimiting seperator */
14543c1707eStholo 	    q++;
14643c1707eStholo 
14743c1707eStholo 	switch (entry_version)
14843c1707eStholo 	{
14943c1707eStholo 	    case 1:
15043c1707eStholo 		/* this means the same normalize_cvsroot we are using was
15143c1707eStholo 		 * used to create this entry.  strcmp is good enough for
15243c1707eStholo 		 * us.
15343c1707eStholo 		 */
15443c1707eStholo 		p = strchr (q, ' ');
15543c1707eStholo 		if (p == NULL)
15643c1707eStholo 		{
15743c1707eStholo 		    if (warn && !really_quiet)
15843c1707eStholo 			error (0, 0, "warning: skipping invalid entry in password file at line %d",
15943c1707eStholo 				linenumber);
16043c1707eStholo 		}
16143c1707eStholo 		else
16243c1707eStholo 		{
16343c1707eStholo 		    *p = '\0';
16443c1707eStholo 		    if (strcmp (cvsroot_canonical, q) == 0)
16543c1707eStholo 			password = p + 1;
16643c1707eStholo 		    *p = ' ';
16743c1707eStholo 		}
16843c1707eStholo 		break;
16943c1707eStholo 	    case ULONG_MAX:
17043c1707eStholo 		if (warn && !really_quiet)
17143c1707eStholo 		{
17243c1707eStholo 		    error (0, errno, "warning: unable to convert version number in password file at line %d",
17343c1707eStholo 			    linenumber);
17443c1707eStholo 		    error (0, 0, "skipping entry");
17543c1707eStholo 		}
17643c1707eStholo 		break;
17743c1707eStholo 	    case 0:
17843c1707eStholo 		if (warn && !really_quiet)
17943c1707eStholo 		    error (0, 0, "warning: skipping entry with invalid version string in password file at line %d",
18043c1707eStholo 			    linenumber);
18143c1707eStholo 		break;
18243c1707eStholo 	    default:
18343c1707eStholo 		if (warn && !really_quiet)
18443c1707eStholo 		    error (0, 0, "warning: skipping entry with unknown version (%lu) in password file at line %d",
18543c1707eStholo 			    entry_version, linenumber);
18643c1707eStholo 		break;
18743c1707eStholo 	}
18843c1707eStholo     }
18943c1707eStholo     else
19043c1707eStholo     {
19143c1707eStholo 	/* No: assume:
19243c1707eStholo 	 *
19343c1707eStholo 	 *	^cvsroot Aencoded_password$
19443c1707eStholo 	 *
19543c1707eStholo 	 * as header comment specifies and parse accordingly
19643c1707eStholo 	 */
19743c1707eStholo 	cvsroot_t *tmp_root;
19843c1707eStholo 	char *tmp_root_canonical;
19943c1707eStholo 
20043c1707eStholo 	p = strchr (linebuf, ' ');
20143c1707eStholo 	if (p == NULL)
20243c1707eStholo 	{
20343c1707eStholo 	    if (warn && !really_quiet)
20443c1707eStholo 		error (0, 0, "warning: skipping invalid entry in password file at line %d", linenumber);
20543c1707eStholo 	    return NULL;;
20643c1707eStholo 	}
20743c1707eStholo 
20843c1707eStholo 	*p = '\0';
20943c1707eStholo 	if ((tmp_root = parse_cvsroot (linebuf)) == NULL)
21043c1707eStholo 	{
21143c1707eStholo 	    if (warn && !really_quiet)
21243c1707eStholo 		error (0, 0, "warning: skipping invalid entry in password file at line %d", linenumber);
21343c1707eStholo 	    *p = ' ';
21443c1707eStholo 	    return NULL;
21543c1707eStholo 	}
21643c1707eStholo 	*p = ' ';
21743c1707eStholo 	tmp_root_canonical = normalize_cvsroot (tmp_root);
21843c1707eStholo 	if (strcmp (cvsroot_canonical, tmp_root_canonical) == 0)
21943c1707eStholo 	    password = p + 1;
22043c1707eStholo 
22143c1707eStholo 	free (tmp_root_canonical);
22243c1707eStholo 	free_cvsroot_t (tmp_root);
22343c1707eStholo     }
22443c1707eStholo 
22543c1707eStholo     return password;
22643c1707eStholo }
22743c1707eStholo 
22843c1707eStholo 
22943c1707eStholo 
23043c1707eStholo /*
23143c1707eStholo  * static char *
23243c1707eStholo  * password_entry_operation (
23343c1707eStholo  * 			     password_entry_operation_t operation,
23443c1707eStholo  * 			     cvsroot_t *root,
23543c1707eStholo  * 			     char *newpassword
23643c1707eStholo  * 			    );
23743c1707eStholo  *
23843c1707eStholo  * Search the password file and depending on the value of operation:
23943c1707eStholo  *
24043c1707eStholo  *	Mode				Action
24143c1707eStholo  *	password_entry_lookup		Return the password
24243c1707eStholo  *	password_entry_delete		Delete the entry from the file, if it exists
24343c1707eStholo  *	password_entry_add		Replace the line with the new one, else append it
24413571821Stholo  *
24513571821Stholo  * Because the user might be accessing multiple repositories, with
24613571821Stholo  * different passwords for each one, the format of ~/.cvspass is:
24713571821Stholo  *
24843c1707eStholo  * [user@]host:[port]/path Aencoded_password
24943c1707eStholo  * [user@]host:[port]/path Aencoded_password
25013571821Stholo  * ...
25113571821Stholo  *
25243c1707eStholo  * New entries are always of the form:
25313571821Stholo  *
25443c1707eStholo  * /1 user@host:port/path Aencoded_password
25543c1707eStholo  *
25643c1707eStholo  * but the old format is supported for backwards compatibility.
25743c1707eStholo  * The entry version string wasn't strictly necessary, but it avoids the
25843c1707eStholo  * overhead of parsing some entries since we know it is already in canonical
25943c1707eStholo  * form and allows room for expansion later, say, if we want to allow spaces
26043c1707eStholo  * and/or other characters to be escaped in the string.  Also, the new entries
26143c1707eStholo  * would have been ignored by old versions of CVS anyhow since those versions
26243c1707eStholo  * didn't know how to parse a port number.
26343c1707eStholo  *
26443c1707eStholo  * The "A" before "encoded_password" is a literal capital A.  It's a
26513571821Stholo  * version number indicating which form of scrambling we're doing on
26613571821Stholo  * the password -- someday we might provide something more secure than
26713571821Stholo  * the trivial encoding we do now, and when that day comes, it would
26813571821Stholo  * be nice to remain backward-compatible.
26913571821Stholo  *
27013571821Stholo  * Like .netrc, the file's permissions are the only thing preventing
27113571821Stholo  * it from being read by others.  Unlike .netrc, we will not be
27213571821Stholo  * fascist about it, at most issuing a warning, and never refusing to
27313571821Stholo  * work.
27443c1707eStholo  *
27543c1707eStholo  * INPUTS
27643c1707eStholo  * 	operation	operation to perform
27743c1707eStholo  * 	root		cvsroot_t to look up
27843c1707eStholo  * 	newpassword	prescrambled new password, for password_entry_add_mode
27943c1707eStholo  *
28043c1707eStholo  * RETURNS
28143c1707eStholo  * 	-1	if password_entry_lookup_mode not specified
28243c1707eStholo  * 	NULL	on failed lookup
28343c1707eStholo  * 	pointer to a copy of the password string otherwise, which the caller is
28443c1707eStholo  * 		responsible for disposing of
28513571821Stholo  */
28643c1707eStholo 
28743c1707eStholo typedef enum password_entry_operation_e {
28843c1707eStholo     password_entry_lookup,
28943c1707eStholo     password_entry_delete,
29043c1707eStholo     password_entry_add
29143c1707eStholo } password_entry_operation_t;
29243c1707eStholo 
29343c1707eStholo static char *
password_entry_operation(operation,root,newpassword)29443c1707eStholo password_entry_operation (operation, root, newpassword)
29543c1707eStholo     password_entry_operation_t operation;
29643c1707eStholo     cvsroot_t *root;
29743c1707eStholo     char *newpassword;
29813571821Stholo {
29913571821Stholo     char *passfile;
30013571821Stholo     FILE *fp;
30143c1707eStholo     char *cvsroot_canonical = NULL;
30243c1707eStholo     char *password = NULL;
3032286d8edStholo     int line_length;
30443c1707eStholo     long line;
30543c1707eStholo     char *linebuf = NULL;
30643c1707eStholo     size_t linebuf_len;
30743c1707eStholo     char *p;
30843c1707eStholo     int save_errno = 0;
30913571821Stholo 
31043c1707eStholo     if (root->method != pserver_method)
31150bf276cStholo     {
31243c1707eStholo 	error (0, 0, "internal error: can only call password_entry_operation with pserver method");
31343c1707eStholo 	error (1, 0, "CVSROOT: %s", root->original);
31450bf276cStholo     }
31550bf276cStholo 
3165c3f1d6aSfgsch     cvsroot_canonical = normalize_cvsroot (root);
3175c3f1d6aSfgsch 
31843c1707eStholo     /* Yes, the method below reads the user's password file twice when we have
31943c1707eStholo      * to delete an entry.  It's inefficient, but we're not talking about a gig of
32043c1707eStholo      * data here.
32143c1707eStholo      */
322c26070a5Stholo 
32313571821Stholo     passfile = construct_cvspass_filename ();
32450bf276cStholo     fp = CVS_FOPEN (passfile, "r");
32513571821Stholo     if (fp == NULL)
32613571821Stholo     {
3275c3f1d6aSfgsch 	if (operation != password_entry_add )
32843c1707eStholo 	    error (0, errno, "failed to open %s for reading", passfile);
3295c3f1d6aSfgsch 	goto process;
33013571821Stholo     }
33113571821Stholo 
33243c1707eStholo     /* Check each line to see if we have this entry already. */
33343c1707eStholo     line = 0;
334*f9bbbf45Sfgsch     while ((line_length = get_line (&linebuf, &linebuf_len, fp)) >= 0)
33513571821Stholo     {
33643c1707eStholo 	line++;
33743c1707eStholo 	password = password_entry_parseline(cvsroot_canonical, 1, line, linebuf);
33843c1707eStholo 	if (password != NULL)
33943c1707eStholo 	    /* this is it!  break out and deal with linebuf */
34043c1707eStholo 	    break;
34143c1707eStholo     }
34243c1707eStholo     if (line_length < 0 && !feof (fp))
34343c1707eStholo     {
34443c1707eStholo 	error (0, errno, "cannot read %s", passfile);
34543c1707eStholo 	goto error_exit;
34643c1707eStholo     }
34743c1707eStholo     if (fclose (fp) < 0)
34843c1707eStholo 	/* not fatal, unless it cascades */
34943c1707eStholo 	error (0, errno, "cannot close %s", passfile);
35043c1707eStholo     fp = NULL;
35143c1707eStholo 
35243c1707eStholo     /* Utter, total, raving paranoia, I know. */
35343c1707eStholo     chmod (passfile, 0600);
35443c1707eStholo 
35543c1707eStholo     /* a copy to return or keep around so we can reuse linebuf */
35643c1707eStholo     if (password != NULL)
35743c1707eStholo     {
35843c1707eStholo 	/* chomp the EOL */
35943c1707eStholo 	p = strchr (password, '\n');
36043c1707eStholo 	if (p != NULL)
36143c1707eStholo 	    *p = '\0';
36243c1707eStholo 	password = xstrdup (password);
36343c1707eStholo     }
36443c1707eStholo 
3655c3f1d6aSfgsch process:
3665c3f1d6aSfgsch 
36743c1707eStholo     /* might as well return now */
36843c1707eStholo     if (operation == password_entry_lookup)
36943c1707eStholo 	goto out;
37043c1707eStholo 
37143c1707eStholo     /* same here */
37243c1707eStholo     if (operation == password_entry_delete && password == NULL)
37343c1707eStholo     {
37443c1707eStholo 	error (0, 0, "Entry not found.");
37543c1707eStholo 	goto out;
37643c1707eStholo     }
37743c1707eStholo 
37843c1707eStholo     /* okay, file errors can simply be fatal from now on since we don't do
37943c1707eStholo      * anything else if we're in lookup mode
38043c1707eStholo      */
38143c1707eStholo 
38243c1707eStholo     /* copy the file with the entry deleted unless we're in add
38343c1707eStholo      * mode and the line we found contains the same password we're supposed to
38443c1707eStholo      * add
38543c1707eStholo      */
38643c1707eStholo     if (!noexec && password != NULL && (operation == password_entry_delete
38743c1707eStholo 	    || (operation == password_entry_add && strcmp (password, newpassword))))
38843c1707eStholo     {
38943c1707eStholo 	long found_at = line;
39043c1707eStholo 	char *tmp_name;
39143c1707eStholo 	FILE *tmp_fp;
39243c1707eStholo 
39343c1707eStholo 	/* open the original file again */
39443c1707eStholo 	fp = CVS_FOPEN (passfile, "r");
39543c1707eStholo 	if (fp == NULL)
39643c1707eStholo 	    error (1, errno, "failed to open %s for reading", passfile);
39743c1707eStholo 
39843c1707eStholo 	/* create and open a temp file */
39943c1707eStholo 	if ((tmp_fp = cvs_temp_file (&tmp_name)) == NULL)
40043c1707eStholo 	    error (1, errno, "unable to open temp file %s", tmp_name);
40143c1707eStholo 
40243c1707eStholo 	line = 0;
403*f9bbbf45Sfgsch 	while ((line_length = get_line (&linebuf, &linebuf_len, fp)) >= 0)
40443c1707eStholo 	{
40543c1707eStholo 	    line++;
40643c1707eStholo 	    if (line < found_at
40743c1707eStholo 		|| (line != found_at
40843c1707eStholo 		    && !password_entry_parseline(cvsroot_canonical, 0, line, linebuf)))
4092286d8edStholo 	    {
4102286d8edStholo 		if (fprintf (tmp_fp, "%s", linebuf) == EOF)
4112286d8edStholo 		{
41243c1707eStholo 		    /* try and clean up anyhow */
41343c1707eStholo 		    error (0, errno, "fatal error: cannot write %s", tmp_name);
41443c1707eStholo 		    if (fclose (tmp_fp) == EOF)
41543c1707eStholo 			error (0, errno, "cannot close %s", tmp_name);
41643c1707eStholo 		    /* call CVS_UNLINK instead of unlink_file since the file
41743c1707eStholo 		     * got created in noexec mode
41843c1707eStholo 		     */
41943c1707eStholo 		    if (CVS_UNLINK (tmp_name) < 0)
42043c1707eStholo 			error (0, errno, "cannot remove %s", tmp_name);
42143c1707eStholo 		    /* but quit so we don't remove all the entries from a
42243c1707eStholo 		     * user's password file accidentally
42343c1707eStholo 		     */
42443c1707eStholo 		    error (1, 0, "exiting");
42543c1707eStholo 		}
4262286d8edStholo 	    }
4272286d8edStholo 	}
4282286d8edStholo 	if (line_length < 0 && !feof (fp))
42943c1707eStholo 	{
4302286d8edStholo 	    error (0, errno, "cannot read %s", passfile);
43143c1707eStholo 	    goto error_exit;
43243c1707eStholo 	}
4332286d8edStholo 	if (fclose (fp) < 0)
43443c1707eStholo 	    /* not fatal, unless it cascades */
4352286d8edStholo 	    error (0, errno, "cannot close %s", passfile);
43643c1707eStholo 	if (fclose (tmp_fp) < 0)
43743c1707eStholo 	    /* not fatal, unless it cascades */
43843c1707eStholo 	    /* FIXME - does copy_file return correct results if the file wasn't
43943c1707eStholo 	     * closed? should this be fatal?
44043c1707eStholo 	     */
44143c1707eStholo 	    error (0, errno, "cannot close %s", tmp_name);
4422286d8edStholo 
4432286d8edStholo 	/* FIXME: rename_file would make more sense (e.g. almost
44443c1707eStholo 	 * always faster).
44543c1707eStholo 	 *
44643c1707eStholo 	 * I don't think so, unless we change the way rename_file works to
44743c1707eStholo 	 * attempt a cp/rm sequence when rename fails since rename doesn't
44843c1707eStholo 	 * work across file systems and it isn't uncommon to have /tmp
44943c1707eStholo 	 * on its own partition.
45043c1707eStholo 	 *
45143c1707eStholo 	 * For that matter, it's probably not uncommon to have a home
45243c1707eStholo 	 * directory on an NFS mount.
45343c1707eStholo 	 */
45450bf276cStholo 	copy_file (tmp_name, passfile);
45543c1707eStholo 	if (CVS_UNLINK (tmp_name) < 0)
456c71bc7e2Stholo 	    error (0, errno, "cannot remove %s", tmp_name);
45750bf276cStholo 	free (tmp_name);
45813571821Stholo     }
45913571821Stholo 
46043c1707eStholo     /* in add mode, if we didn't find an entry or found an entry with a
46143c1707eStholo      * different password, append the new line
46243c1707eStholo      */
46343c1707eStholo     if (!noexec && operation == password_entry_add
46443c1707eStholo 	    && (password == NULL || strcmp (password, newpassword)))
46543c1707eStholo     {
46643c1707eStholo 	if ((fp = CVS_FOPEN (passfile, "a")) == NULL)
46743c1707eStholo 	    error (1, errno, "could not open %s for writing", passfile);
46843c1707eStholo 
46943c1707eStholo 	if (fprintf (fp, "/1 %s %s\n", cvsroot_canonical, newpassword) == EOF)
47043c1707eStholo 	    error (1, errno, "cannot write %s", passfile);
4712286d8edStholo 	if (fclose (fp) < 0)
4722286d8edStholo 	    error (0, errno, "cannot close %s", passfile);
47313571821Stholo     }
47413571821Stholo 
47513571821Stholo     /* Utter, total, raving paranoia, I know. */
47613571821Stholo     chmod (passfile, 0600);
47743c1707eStholo 
47843c1707eStholo     if (password)
47943c1707eStholo     {
48043c1707eStholo 	free (password);
48143c1707eStholo 	password = NULL;
48243c1707eStholo     }
48343c1707eStholo     if (linebuf)
48443c1707eStholo 	free (linebuf);
48543c1707eStholo 
48643c1707eStholo out:
48743c1707eStholo     free (cvsroot_canonical);
48843c1707eStholo     free (passfile);
48943c1707eStholo     return password;
49043c1707eStholo 
49143c1707eStholo error_exit:
49243c1707eStholo     /* just exit when we're not in lookup mode */
49343c1707eStholo     if (operation != password_entry_lookup)
49443c1707eStholo 	error (1, 0, "fatal error: exiting");
49543c1707eStholo     /* clean up and exit in lookup mode so we can try a login with a NULL
49643c1707eStholo      * password anyhow in case that's what we would have found
49743c1707eStholo      */
49843c1707eStholo     save_errno = errno;
49943c1707eStholo     if (fp != NULL)
50043c1707eStholo     {
50143c1707eStholo 	/* Utter, total, raving paranoia, I know. */
50243c1707eStholo 	chmod (passfile, 0600);
50343c1707eStholo 	if(fclose (fp) < 0)
50443c1707eStholo 	    error (0, errno, "cannot close %s", passfile);
50543c1707eStholo     }
50643c1707eStholo     if (linebuf)
50743c1707eStholo 	free (linebuf);
50843c1707eStholo     if (cvsroot_canonical)
50943c1707eStholo 	free (cvsroot_canonical);
51043c1707eStholo     free (passfile);
51143c1707eStholo     errno = save_errno;
51243c1707eStholo     return NULL;
51343c1707eStholo }
51443c1707eStholo 
51543c1707eStholo 
51643c1707eStholo 
51743c1707eStholo /* Prompt for a password, and store it in the file "CVS/.cvspass".
51843c1707eStholo  */
51943c1707eStholo 
52043c1707eStholo static const char *const login_usage[] =
52143c1707eStholo {
52243c1707eStholo     "Usage: %s %s\n",
52343c1707eStholo     "(Specify the --help global option for a list of other help options)\n",
52443c1707eStholo     NULL
52543c1707eStholo };
52643c1707eStholo 
52743c1707eStholo int
login(argc,argv)52843c1707eStholo login (argc, argv)
52943c1707eStholo     int argc;
53043c1707eStholo     char **argv;
53143c1707eStholo {
53243c1707eStholo     char *typed_password;
53343c1707eStholo     char *cvsroot_canonical;
53443c1707eStholo 
53543c1707eStholo     if (argc < 0)
53643c1707eStholo 	usage (login_usage);
53743c1707eStholo 
53843c1707eStholo     if (current_parsed_root->method != pserver_method)
53943c1707eStholo     {
54043c1707eStholo 	error (0, 0, "can only use `login' command with the 'pserver' method");
54143c1707eStholo 	error (1, 0, "CVSROOT: %s", current_parsed_root->original);
54243c1707eStholo     }
54343c1707eStholo 
54443c1707eStholo     cvsroot_canonical = normalize_cvsroot(current_parsed_root);
54543c1707eStholo     printf ("Logging in to %s\n", cvsroot_canonical);
54643c1707eStholo     fflush (stdout);
54743c1707eStholo 
54843c1707eStholo     if (current_parsed_root->password)
54943c1707eStholo     {
55043c1707eStholo 	typed_password = scramble (current_parsed_root->password);
55143c1707eStholo     }
55243c1707eStholo     else
55343c1707eStholo     {
55443c1707eStholo 	char *tmp;
55543c1707eStholo 	tmp = GETPASS ("CVS password: ");
55643c1707eStholo 	typed_password = scramble (tmp);
55743c1707eStholo 	memset (tmp, 0, strlen (tmp));
55843c1707eStholo     }
55943c1707eStholo 
56043c1707eStholo     /* Force get_cvs_password() to use this one (when the client
56143c1707eStholo      * confirms the new password with the server), instead of
56243c1707eStholo      * consulting the file.  We make a new copy because cvs_password
56343c1707eStholo      * will get zeroed by connect_to_server().  */
56443c1707eStholo     cvs_password = xstrdup (typed_password);
56543c1707eStholo 
56643c1707eStholo     connect_to_pserver (NULL, NULL, 1, 0);
56743c1707eStholo 
56843c1707eStholo     password_entry_operation (password_entry_add, current_parsed_root, typed_password);
56943c1707eStholo 
57013571821Stholo     memset (typed_password, 0, strlen (typed_password));
57113571821Stholo     free (typed_password);
57213571821Stholo 
57313571821Stholo     free (cvs_password);
57443c1707eStholo     free (cvsroot_canonical);
57513571821Stholo     cvs_password = NULL;
57643c1707eStholo 
57713571821Stholo     return 0;
57813571821Stholo }
57913571821Stholo 
58013571821Stholo /* Returns the _scrambled_ password.  The server must descramble
581e77048c1Stholo    before hashing and comparing.  If password file not found, or
582e77048c1Stholo    password not found in the file, just return NULL. */
58313571821Stholo char *
get_cvs_password()58413571821Stholo get_cvs_password ()
58513571821Stholo {
58643c1707eStholo     if (current_parsed_root->password)
58743c1707eStholo 	return (scramble(current_parsed_root->password));
58813571821Stholo 
58913571821Stholo     /* If someone (i.e., login()) is calling connect_to_pserver() out of
59013571821Stholo        context, then assume they have supplied the correct, scrambled
59113571821Stholo        password. */
59213571821Stholo     if (cvs_password)
59313571821Stholo 	return cvs_password;
59413571821Stholo 
595461cc63eStholo     if (getenv ("CVS_PASSWORD") != NULL)
59613571821Stholo     {
597461cc63eStholo 	/* In previous versions of CVS one could specify a password in
59843c1707eStholo 	 * CVS_PASSWORD.  This is a bad idea, because in BSD variants
59943c1707eStholo 	 * of unix anyone can see the environment variable with 'ps'.
60043c1707eStholo 	 * But for users who were using that feature we want to at
60143c1707eStholo 	 * least let them know what is going on.  After printing this
60243c1707eStholo 	 * warning, we should fall through to the regular error where
60343c1707eStholo 	 * we tell them to run "cvs login" (unless they already ran
60443c1707eStholo 	 * it, of course).
60543c1707eStholo 	 */
606461cc63eStholo 	 error (0, 0, "CVS_PASSWORD is no longer supported; ignored");
60713571821Stholo     }
60813571821Stholo 
60943c1707eStholo     if (current_parsed_root->method != pserver_method)
61050bf276cStholo     {
61143c1707eStholo 	error (0, 0, "can only call get_cvs_password with pserver method");
61243c1707eStholo 	error (1, 0, "CVSROOT: %s", current_parsed_root->original);
61350bf276cStholo     }
61450bf276cStholo 
61543c1707eStholo     return password_entry_operation (password_entry_lookup, current_parsed_root, NULL);
61613571821Stholo }
61713571821Stholo 
618780d15dfStholo static const char *const logout_usage[] =
619780d15dfStholo {
620780d15dfStholo     "Usage: %s %s\n",
6212286d8edStholo     "(Specify the --help global option for a list of other help options)\n",
622780d15dfStholo     NULL
623780d15dfStholo };
624780d15dfStholo 
625e77048c1Stholo /* Remove any entry for the CVSRoot repository found in .cvspass. */
626780d15dfStholo int
logout(argc,argv)627780d15dfStholo logout (argc, argv)
628780d15dfStholo     int argc;
629780d15dfStholo     char **argv;
630780d15dfStholo {
63143c1707eStholo     char *cvsroot_canonical;
632780d15dfStholo 
633780d15dfStholo     if (argc < 0)
634780d15dfStholo 	usage (logout_usage);
635780d15dfStholo 
63643c1707eStholo     if (current_parsed_root->method != pserver_method)
637780d15dfStholo     {
638780d15dfStholo 	error (0, 0, "can only use pserver method with `logout' command");
63943c1707eStholo 	error (1, 0, "CVSROOT: %s", current_parsed_root->original);
640780d15dfStholo     }
641780d15dfStholo 
642780d15dfStholo     /* Hmm.  Do we want a variant of this command which deletes _all_
643780d15dfStholo        the entries from the current .cvspass?  Might be easier to
644780d15dfStholo        remember than "rm ~/.cvspass" but then again if people are
645780d15dfStholo        mucking with HOME (common in Win95 as the system doesn't set
646780d15dfStholo        it), then this variant of "cvs logout" might give a false sense
647780d15dfStholo        of security, in that it wouldn't delete entries from any
648780d15dfStholo        .cvspass files but the current one.  */
649780d15dfStholo 
65043c1707eStholo     if (!quiet)
65143c1707eStholo     {
65243c1707eStholo 	cvsroot_canonical = normalize_cvsroot(current_parsed_root);
65343c1707eStholo 	printf ("Logging out of %s\n", cvsroot_canonical);
654780d15dfStholo 	fflush (stdout);
65543c1707eStholo 	free (cvsroot_canonical);
656780d15dfStholo     }
657e77048c1Stholo 
65843c1707eStholo     password_entry_operation (password_entry_delete, current_parsed_root, NULL);
659e77048c1Stholo 
660780d15dfStholo     return 0;
661780d15dfStholo }
662780d15dfStholo 
66313571821Stholo #endif /* AUTH_CLIENT_SUPPORT from beginning of file. */
664