Home | History | Annotate | Line # | Download | only in src
      1 /*
      2  * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
      3  *
      4  * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
      5  *                                  and others.
      6  *
      7  * Portions Copyright (c) 1995, Cyclic Software, Bloomington, IN, USA
      8  *
      9  * You may distribute under the terms of the GNU General Public License as
     10  * specified in the README file that comes with CVS.
     11  *
     12  * Allow user to log in for an authenticating server.
     13  */
     14 #include <sys/cdefs.h>
     15 __RCSID("$NetBSD: login.c,v 1.3 2016/05/17 14:00:09 christos Exp $");
     16 
     17 #include "cvs.h"
     18 #include "getline.h"
     19 
     20 /* There seems to be very little agreement on which system header
     21    getpass is declared in.  With a lot of fancy autoconfiscation,
     22    we could perhaps detect this, but for now we'll just rely on
     23    _CRAY, since Cray is perhaps the only system on which our own
     24    declaration won't work (some Crays declare the 2#$@% thing as
     25    varadic, believe it or not).  On Cray, getpass will be declared
     26    in either stdlib.h or unistd.h.  */
     27 #include "getpass.h"
     28 
     29 #ifdef AUTH_CLIENT_SUPPORT   /* This covers the rest of the file. */
     30 
     31 
     32 #ifndef CVS_PASSWORD_FILE
     33 #define CVS_PASSWORD_FILE ".cvspass"
     34 #endif
     35 
     36 /* If non-NULL, get_cvs_password() will just return this. */
     37 static char *cvs_password = NULL;
     38 
     39 static char *construct_cvspass_filename (void);
     40 
     41 /* The return value will need to be freed. */
     42 static char *
     43 construct_cvspass_filename (void)
     44 {
     45     char *homedir;
     46     char *passfile;
     47 
     48     /* Environment should override file. */
     49     if ((passfile = getenv ("CVS_PASSFILE")) != NULL)
     50 	return xstrdup (passfile);
     51 
     52     /* Construct absolute pathname to user's password file. */
     53     /* todo: does this work under OS/2 ? */
     54     homedir = get_homedir ();
     55     if (! homedir)
     56     {
     57 	/* FIXME?  This message confuses a lot of users, at least
     58 	   on Win95 (which doesn't set HOMEDRIVE and HOMEPATH like
     59 	   NT does).  I suppose the answer for Win95 is to store the
     60 	   passwords in the registry or something (??).  And .cvsrc
     61 	   and such too?  Wonder what WinCVS does (about .cvsrc, the
     62 	   right thing for a GUI is to just store the password in
     63 	   memory only)...  */
     64 	error (1, 0, "could not find out home directory");
     65 	return NULL;
     66     }
     67 
     68     passfile = strcat_filename_onto_homedir (homedir, CVS_PASSWORD_FILE);
     69 
     70     /* Safety first and last, Scouts. */
     71     if (isfile (passfile))
     72 	/* xchmod() is too polite. */
     73 	chmod (passfile, 0600);
     74 
     75     return passfile;
     76 }
     77 
     78 
     79 
     80 /*
     81  * static char *
     82  * password_entry_parseline (
     83  *			      const char *cvsroot_canonical,
     84  *			      const unsigned char warn,
     85  *			      const int linenumber,
     86  *			      char *linebuf
     87  *			     );
     88  *
     89  * Internal function used by password_entry_operation.  Parse a single line
     90  * from a ~/.cvsroot password file and return a pointer to the password if the
     91  * line refers to the same cvsroot as cvsroot_canonical
     92  *
     93  * INPUTS
     94  *	cvsroot_canonical	the root we are looking for
     95  *	warn			Boolean: print warnings for invalid lines?
     96  *	linenumber		the line number for error messages
     97  *	linebuf			the current line
     98  *
     99  * RETURNS
    100  * 	NULL			if the line doesn't match
    101  * 	char *password		as a pointer into linebuf
    102  *
    103  * NOTES
    104  *	This function temporarily alters linebuf, so it isn't thread safe when
    105  *	called on the same linebuf
    106  */
    107 static char *
    108 password_entry_parseline (const char *cvsroot_canonical,
    109 			  const unsigned char warn, const int linenumber,
    110 			  char *linebuf)
    111 {
    112     char *password = NULL;
    113     char *p;
    114 
    115     /* look for '^/' */
    116     if (*linebuf == '/')
    117     {
    118 	/* Yes: slurp '^/\d+\D' and parse the rest of the line according to
    119 	 * version number
    120 	 */
    121 	char *q;
    122 	unsigned long int entry_version = 0 /* Placate -Wall.  */;
    123 
    124 	if (isspace(*(linebuf + 1)))
    125 	    /* special case since strtoul ignores leading white space */
    126 	    q = linebuf + 1;
    127 	else
    128 	    entry_version = strtoul (linebuf + 1, &q, 10);
    129 
    130 	if (q != linebuf + 1)
    131 	    /* assume a delimiting seperator */
    132 	    q++;
    133 	/* else, no valid digits found by strtoul */
    134 
    135 	switch (entry_version)
    136 	{
    137 	    case 1:
    138 		/* this means the same normalize_cvsroot we are using was
    139 		 * used to create this entry.  strcmp is good enough for
    140 		 * us.
    141 		 */
    142 		p = strchr (q, ' ');
    143 		if (p == NULL)
    144 		{
    145 		    if (warn && !really_quiet)
    146 			error (0, 0, "warning: skipping invalid entry in password file at line %d",
    147 				linenumber);
    148 		}
    149 		else
    150 		{
    151 		    *p = '\0';
    152 		    if (strcmp (cvsroot_canonical, q) == 0)
    153 			password = p + 1;
    154 		    *p = ' ';
    155 		}
    156 		break;
    157 	    case ULONG_MAX:
    158 		if (warn && !really_quiet)
    159 		{
    160 		    error (0, errno, "warning: unable to convert version number in password file at line %d",
    161 			    linenumber);
    162 		    error (0, 0, "skipping entry");
    163 		}
    164 		break;
    165 	    case 0:
    166 		if (warn && !really_quiet)
    167 		    error (0, 0, "warning: skipping entry with invalid version string in password file at line %d",
    168 			    linenumber);
    169 		break;
    170 	    default:
    171 		if (warn && !really_quiet)
    172 		    error (0, 0, "warning: skipping entry with unknown version (%lu) in password file at line %d",
    173 			    entry_version, linenumber);
    174 		break;
    175 	}
    176     }
    177     else
    178     {
    179 	/* No: assume:
    180 	 *
    181 	 *	^cvsroot Aencoded_password$
    182 	 *
    183 	 * as header comment specifies and parse accordingly
    184 	 */
    185 	cvsroot_t *tmp_root;
    186 	char *tmp_root_canonical;
    187 
    188 	p = strchr (linebuf, ' ');
    189 	if (p == NULL)
    190 	{
    191 	    if (warn && !really_quiet)
    192 		error (0, 0, "warning: skipping invalid entry in password file at line %d", linenumber);
    193 	    return NULL;;
    194 	}
    195 
    196 	*p = '\0';
    197 	if ((tmp_root = parse_cvsroot (linebuf)) == NULL)
    198 	{
    199 	    if (warn && !really_quiet)
    200 		error (0, 0, "warning: skipping invalid entry in password file at line %d", linenumber);
    201 	    *p = ' ';
    202 	    return NULL;
    203 	}
    204 	*p = ' ';
    205 	tmp_root_canonical = normalize_cvsroot (tmp_root);
    206 	if (strcmp (cvsroot_canonical, tmp_root_canonical) == 0)
    207 	    password = p + 1;
    208 
    209 	free (tmp_root_canonical);
    210     }
    211 
    212     return password;
    213 }
    214 
    215 
    216 
    217 /*
    218  * static char *
    219  * password_entry_operation (
    220  * 			     password_entry_operation_t operation,
    221  * 			     cvsroot_t *root,
    222  * 			     char *newpassword
    223  * 			    );
    224  *
    225  * Search the password file and depending on the value of operation:
    226  *
    227  *	Mode				Action
    228  *	password_entry_lookup		Return the password
    229  *	password_entry_delete		Delete the entry from the file, if it
    230  *                                      exists.
    231  *	password_entry_add		Replace the line with the new one, else
    232  *                                      append it.
    233  *
    234  * Because the user might be accessing multiple repositories, with
    235  * different passwords for each one, the format of ~/.cvspass is:
    236  *
    237  * [user@]host:[port]/path Aencoded_password
    238  * [user@]host:[port]/path Aencoded_password
    239  * ...
    240  *
    241  * New entries are always of the form:
    242  *
    243  * /1 user@host:port/path Aencoded_password
    244  *
    245  * but the old format is supported for backwards compatibility.
    246  * The entry version string wasn't strictly necessary, but it avoids the
    247  * overhead of parsing some entries since we know it is already in canonical
    248  * form and allows room for expansion later, say, if we want to allow spaces
    249  * and/or other characters to be escaped in the string.  Also, the new entries
    250  * would have been ignored by old versions of CVS anyhow since those versions
    251  * didn't know how to parse a port number.
    252  *
    253  * The "A" before "encoded_password" is a literal capital A.  It's a
    254  * version number indicating which form of scrambling we're doing on
    255  * the password -- someday we might provide something more secure than
    256  * the trivial encoding we do now, and when that day comes, it would
    257  * be nice to remain backward-compatible.
    258  *
    259  * Like .netrc, the file's permissions are the only thing preventing
    260  * it from being read by others.  Unlike .netrc, we will not be
    261  * fascist about it, at most issuing a warning, and never refusing to
    262  * work.
    263  *
    264  * INPUTS
    265  * 	operation	operation to perform
    266  * 	root		cvsroot_t to look up
    267  * 	newpassword	prescrambled new password, for password_entry_add_mode
    268  *
    269  * RETURNS
    270  * 	-1	if password_entry_lookup_mode not specified
    271  * 	NULL	on failed lookup
    272  * 	pointer to a copy of the password string otherwise, which the caller is
    273  * 		responsible for disposing of
    274  */
    275 
    276 typedef enum password_entry_operation_e {
    277     password_entry_lookup,
    278     password_entry_delete,
    279     password_entry_add
    280 } password_entry_operation_t;
    281 
    282 static char *
    283 password_entry_operation (password_entry_operation_t operation, cvsroot_t *root, char *newpassword)
    284 {
    285     char *passfile;
    286     FILE *fp;
    287     char *cvsroot_canonical = NULL;
    288     char *password = NULL;
    289     int line_length;
    290     long line = -1;
    291     char *linebuf = NULL;
    292     size_t linebuf_len;
    293     char *p;
    294     int save_errno = 0;
    295 
    296     if (root->method != pserver_method)
    297     {
    298 	error (0, 0, "\
    299 internal error: can only call password_entry_operation with pserver method");
    300 	error (1, 0, "CVSROOT: %s", root->original);
    301     }
    302 
    303     cvsroot_canonical = normalize_cvsroot (root);
    304 
    305     /* Yes, the method below reads the user's password file twice when we have
    306      * to delete an entry.  It's inefficient, but we're not talking about a gig of
    307      * data here.
    308      */
    309 
    310     passfile = construct_cvspass_filename ();
    311     fp = CVS_FOPEN (passfile, "r");
    312     if (fp == NULL)
    313     {
    314 	error (0, errno, "warning: failed to open %s for reading", passfile);
    315 	goto process;
    316     }
    317 
    318     /* Check each line to see if we have this entry already. */
    319     line = 0L;
    320     while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0)
    321     {
    322 	line++;
    323 	password = password_entry_parseline (cvsroot_canonical, 1, line,
    324                                              linebuf);
    325 	if (password != NULL)
    326 	    /* this is it!  break out and deal with linebuf */
    327 	    break;
    328     }
    329     if (line_length < 0 && !feof (fp))
    330     {
    331 	error (0, errno, "cannot read %s", passfile);
    332 	goto error_exit;
    333     }
    334     if (fclose (fp) < 0)
    335 	/* not fatal, unless it cascades */
    336 	error (0, errno, "cannot close %s", passfile);
    337     fp = NULL;
    338 
    339     /* Utter, total, raving paranoia, I know. */
    340     chmod (passfile, 0600);
    341 
    342     /* a copy to return or keep around so we can reuse linebuf */
    343     if (password != NULL)
    344     {
    345 	/* chomp the EOL */
    346 	p = strchr (password, '\n');
    347 	if (p != NULL)
    348 	    *p = '\0';
    349 	password = xstrdup (password);
    350     }
    351 
    352 process:
    353 
    354     /* might as well return now */
    355     if (operation == password_entry_lookup)
    356 	goto out;
    357 
    358     /* same here */
    359     if (operation == password_entry_delete && password == NULL)
    360     {
    361 	error (0, 0, "Entry not found.");
    362 	goto out;
    363     }
    364 
    365     /* okay, file errors can simply be fatal from now on since we don't do
    366      * anything else if we're in lookup mode
    367      */
    368 
    369     /* copy the file with the entry deleted unless we're in add
    370      * mode and the line we found contains the same password we're supposed to
    371      * add
    372      */
    373     if (!noexec && password != NULL && (operation == password_entry_delete
    374         || (operation == password_entry_add
    375             && strcmp (password, newpassword))))
    376     {
    377 	long found_at = line;
    378 	char *tmp_name;
    379 	FILE *tmp_fp;
    380 
    381 	/* open the original file again */
    382 	fp = CVS_FOPEN (passfile, "r");
    383 	if (fp == NULL)
    384 	    error (1, errno, "failed to open %s for reading", passfile);
    385 
    386 	/* create and open a temp file */
    387 	if ((tmp_fp = cvs_temp_file (&tmp_name)) == NULL)
    388 	    error (1, errno, "unable to open temp file %s", tmp_name);
    389 
    390 	line = 0L;
    391 	while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0)
    392 	{
    393 	    line++;
    394 	    if (line < found_at
    395 		|| (line != found_at
    396 		    && !password_entry_parseline (cvsroot_canonical, 0, line,
    397                                                   linebuf)))
    398 	    {
    399 		if (fprintf (tmp_fp, "%s", linebuf) == EOF)
    400 		{
    401 		    /* try and clean up anyhow */
    402 		    error (0, errno, "fatal error: cannot write %s", tmp_name);
    403 		    if (fclose (tmp_fp) == EOF)
    404 			error (0, errno, "cannot close %s", tmp_name);
    405 		    /* call CVS_UNLINK instead of unlink_file since the file
    406 		     * got created in noexec mode
    407 		     */
    408 		    if (CVS_UNLINK (tmp_name) < 0)
    409 			error (0, errno, "cannot remove %s", tmp_name);
    410 		    /* but quit so we don't remove all the entries from a
    411 		     * user's password file accidentally
    412 		     */
    413 		    error (1, 0, "exiting");
    414 		}
    415 	    }
    416 	}
    417 	if (line_length < 0 && !feof (fp))
    418 	{
    419 	    error (0, errno, "cannot read %s", passfile);
    420 	    goto error_exit;
    421 	}
    422 	if (fclose (fp) < 0)
    423 	    /* not fatal, unless it cascades */
    424 	    error (0, errno, "cannot close %s", passfile);
    425 	if (fclose (tmp_fp) < 0)
    426 	    /* not fatal, unless it cascades */
    427 	    /* FIXME - does copy_file return correct results if the file wasn't
    428 	     * closed? should this be fatal?
    429 	     */
    430 	    error (0, errno, "cannot close %s", tmp_name);
    431 
    432 	/* FIXME: rename_file would make more sense (e.g. almost
    433 	 * always faster).
    434 	 *
    435 	 * I don't think so, unless we change the way rename_file works to
    436 	 * attempt a cp/rm sequence when rename fails since rename doesn't
    437 	 * work across file systems and it isn't uncommon to have /tmp
    438 	 * on its own partition.
    439 	 *
    440 	 * For that matter, it's probably not uncommon to have a home
    441 	 * directory on an NFS mount.
    442 	 */
    443 	copy_file (tmp_name, passfile);
    444 	if (CVS_UNLINK (tmp_name) < 0)
    445 	    error (0, errno, "cannot remove %s", tmp_name);
    446 	free (tmp_name);
    447     }
    448 
    449     /* in add mode, if we didn't find an entry or found an entry with a
    450      * different password, append the new line
    451      */
    452     if (!noexec && operation == password_entry_add
    453 	    && (password == NULL || strcmp (password, newpassword)))
    454     {
    455 	if ((fp = CVS_FOPEN (passfile, "a")) == NULL)
    456 	    error (1, errno, "could not open %s for writing", passfile);
    457 
    458 	if (fprintf (fp, "/1 %s %s\n", cvsroot_canonical, newpassword) == EOF)
    459 	    error (1, errno, "cannot write %s", passfile);
    460 	if (fclose (fp) < 0)
    461 	    error (1, errno, "cannot close %s", passfile);
    462     }
    463 
    464     /* Utter, total, raving paranoia, I know. */
    465     chmod (passfile, 0600);
    466 
    467     if (password)
    468     {
    469 	free (password);
    470 	password = NULL;
    471     }
    472     if (linebuf)
    473 	free (linebuf);
    474 
    475 out:
    476     free (cvsroot_canonical);
    477     free (passfile);
    478     return password;
    479 
    480 error_exit:
    481     /* just exit when we're not in lookup mode */
    482     if (operation != password_entry_lookup)
    483 	error (1, 0, "fatal error: exiting");
    484     /* clean up and exit in lookup mode so we can try a login with a NULL
    485      * password anyhow in case that's what we would have found
    486      */
    487     save_errno = errno;
    488     if (fp != NULL)
    489     {
    490 	/* Utter, total, raving paranoia, I know. */
    491 	chmod (passfile, 0600);
    492 	if(fclose (fp) < 0)
    493 	    error (0, errno, "cannot close %s", passfile);
    494     }
    495     if (linebuf)
    496 	free (linebuf);
    497     if (cvsroot_canonical)
    498 	free (cvsroot_canonical);
    499     free (passfile);
    500     errno = save_errno;
    501     return NULL;
    502 }
    503 
    504 
    505 
    506 /* Prompt for a password, and store it in the file "CVS/.cvspass".
    507  */
    508 
    509 static const char *const login_usage[] =
    510 {
    511     "Usage: %s %s\n",
    512     "(Specify the --help global option for a list of other help options)\n",
    513     NULL
    514 };
    515 
    516 int
    517 login (int argc, char **argv)
    518 {
    519     char *typed_password;
    520     char *cvsroot_canonical;
    521 
    522     if (argc < 0)
    523 	usage (login_usage);
    524 
    525     if (current_parsed_root->method != pserver_method)
    526     {
    527 	error (0, 0, "can only use `login' command with the 'pserver' method");
    528 	error (1, 0, "CVSROOT: %s", current_parsed_root->original);
    529     }
    530 
    531     cvsroot_canonical = normalize_cvsroot(current_parsed_root);
    532     printf ("Logging in to %s\n", cvsroot_canonical);
    533     fflush (stdout);
    534 
    535     if (current_parsed_root->password)
    536     {
    537 	typed_password = scramble (current_parsed_root->password);
    538     }
    539     else
    540     {
    541 	char *tmp;
    542 	tmp = getpass ("CVS password: ");
    543 	/* Must deal with a NULL return value here.  I haven't managed to
    544 	 * disconnect the CVS process from the tty and force a NULL return
    545 	 * in sanity.sh, but the Linux version of getpass is documented
    546 	 * to return NULL when it can't open /dev/tty...
    547 	 */
    548 	if (!tmp) error (1, errno, "login: Failed to read password.");
    549 	typed_password = scramble (tmp);
    550 	memset (tmp, 0, strlen (tmp));
    551     }
    552 
    553     /* Force get_cvs_password() to use this one (when the client
    554      * confirms the new password with the server), instead of
    555      * consulting the file.  We make a new copy because cvs_password
    556      * will get zeroed by connect_to_server().  */
    557     cvs_password = xstrdup (typed_password);
    558 
    559     connect_to_pserver (current_parsed_root, NULL, NULL, 1, 0);
    560 
    561     password_entry_operation (password_entry_add, current_parsed_root,
    562                               typed_password);
    563 
    564     free_cvs_password (typed_password);
    565     free (cvsroot_canonical);
    566 
    567     return 0;
    568 }
    569 
    570 /* Free the password returned by get_cvs_password() and also free the
    571  * saved cvs_password if they are different pointers. Be paranoid
    572  * about the in-memory copy of the password and overwrite it with zero
    573  * bytes before doing the free().
    574  */
    575 void
    576 free_cvs_password (char *password)
    577 {
    578     if (password && password != cvs_password)
    579     {
    580 	memset (password, 0, strlen (password));
    581 	free (password);
    582     }
    583 
    584     if (cvs_password)
    585     {
    586 	memset (cvs_password, 0, strlen (cvs_password));
    587 	free (cvs_password);
    588 	cvs_password = NULL;
    589     }
    590 }
    591 
    592 
    593 /* Returns the _scrambled_ password.  The server must descramble
    594    before hashing and comparing.  If password file not found, or
    595    password not found in the file, just return NULL. */
    596 char *
    597 get_cvs_password (void)
    598 {
    599     if (current_parsed_root->password)
    600 	return scramble (current_parsed_root->password);
    601 
    602     /* If someone (i.e., login()) is calling connect_to_pserver() out of
    603        context, then assume they have supplied the correct, scrambled
    604        password. */
    605     if (cvs_password)
    606 	return xstrdup (cvs_password);
    607 
    608     if (getenv ("CVS_PASSWORD") != NULL)
    609     {
    610 	/* In previous versions of CVS one could specify a password in
    611 	 * CVS_PASSWORD.  This is a bad idea, because in BSD variants
    612 	 * of unix anyone can see the environment variable with 'ps'.
    613 	 * But for users who were using that feature we want to at
    614 	 * least let them know what is going on.  After printing this
    615 	 * warning, we should fall through to the regular error where
    616 	 * we tell them to run "cvs login" (unless they already ran
    617 	 * it, of course).
    618 	 */
    619 	 error (0, 0, "CVS_PASSWORD is no longer supported; ignored");
    620     }
    621 
    622     if (current_parsed_root->method != pserver_method)
    623     {
    624 	error (0, 0, "can only call get_cvs_password with pserver method");
    625 	error (1, 0, "CVSROOT: %s", current_parsed_root->original);
    626     }
    627 
    628     return password_entry_operation (password_entry_lookup,
    629                                      current_parsed_root, NULL);
    630 }
    631 
    632 
    633 
    634 static const char *const logout_usage[] =
    635 {
    636     "Usage: %s %s\n",
    637     "(Specify the --help global option for a list of other help options)\n",
    638     NULL
    639 };
    640 
    641 /* Remove any entry for the CVSRoot repository found in .cvspass. */
    642 int
    643 logout (int argc, char **argv)
    644 {
    645     char *cvsroot_canonical;
    646 
    647     if (argc < 0)
    648 	usage (logout_usage);
    649 
    650     if (current_parsed_root->method != pserver_method)
    651     {
    652 	error (0, 0, "can only use pserver method with `logout' command");
    653 	error (1, 0, "CVSROOT: %s", current_parsed_root->original);
    654     }
    655 
    656     /* Hmm.  Do we want a variant of this command which deletes _all_
    657        the entries from the current .cvspass?  Might be easier to
    658        remember than "rm ~/.cvspass" but then again if people are
    659        mucking with HOME (common in Win95 as the system doesn't set
    660        it), then this variant of "cvs logout" might give a false sense
    661        of security, in that it wouldn't delete entries from any
    662        .cvspass files but the current one.  */
    663 
    664     if (!quiet)
    665     {
    666 	cvsroot_canonical = normalize_cvsroot(current_parsed_root);
    667 	printf ("Logging out of %s\n", cvsroot_canonical);
    668 	fflush (stdout);
    669 	free (cvsroot_canonical);
    670     }
    671 
    672     password_entry_operation (password_entry_delete, current_parsed_root, NULL);
    673 
    674     return 0;
    675 }
    676 
    677 #endif /* AUTH_CLIENT_SUPPORT from beginning of file. */
    678