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