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