login.c revision 177404
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 * $FreeBSD: head/contrib/cvs/src/login.c 177404 2008-03-19 15:11:46Z obrien $
15 */
16
17#include "cvs.h"
18#include "getline.h"
19
20#ifdef AUTH_CLIENT_SUPPORT   /* This covers the rest of the file. */
21
22/* There seems to be very little agreement on which system header
23   getpass is declared in.  With a lot of fancy autoconfiscation,
24   we could perhaps detect this, but for now we'll just rely on
25   _CRAY, since Cray is perhaps the only system on which our own
26   declaration won't work (some Crays declare the 2#$@% thing as
27   varadic, believe it or not).  On Cray, getpass will be declared
28   in either stdlib.h or unistd.h.  */
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 PROTO ((void));
38
39/* The return value will need to be freed. */
40static char *
41construct_cvspass_filename ()
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 (char *) 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 (cvsroot_canonical, warn, linenumber, linebuf)
107    const char *cvsroot_canonical;
108    const unsigned char warn;
109    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 version number */
119	char *q;
120	unsigned long int entry_version = 0;
121
122	if (isspace(*(linebuf + 1)))
123	{
124	    /* special case since strtoul ignores leading white space */
125	    q = linebuf + 1;
126	}
127	else
128	{
129	    entry_version = strtoul (linebuf + 1, &q, 10);
130	    if (q != linebuf + 1)
131		/* assume a delimiting seperator */
132		q++;
133	}
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	free_cvsroot_t (tmp_root);
211    }
212
213    return password;
214}
215
216
217
218/*
219 * static char *
220 * password_entry_operation (
221 * 			     password_entry_operation_t operation,
222 * 			     cvsroot_t *root,
223 * 			     char *newpassword
224 * 			    );
225 *
226 * Search the password file and depending on the value of operation:
227 *
228 *	Mode				Action
229 *	password_entry_lookup		Return the password
230 *	password_entry_delete		Delete the entry from the file, if it
231 *                                      exists.
232 *	password_entry_add		Replace the line with the new one, else
233 *                                      append it.
234 *
235 * Because the user might be accessing multiple repositories, with
236 * different passwords for each one, the format of ~/.cvspass is:
237 *
238 * [user@]host:[port]/path Aencoded_password
239 * [user@]host:[port]/path Aencoded_password
240 * ...
241 *
242 * New entries are always of the form:
243 *
244 * /1 user@host:port/path Aencoded_password
245 *
246 * but the old format is supported for backwards compatibility.
247 * The entry version string wasn't strictly necessary, but it avoids the
248 * overhead of parsing some entries since we know it is already in canonical
249 * form and allows room for expansion later, say, if we want to allow spaces
250 * and/or other characters to be escaped in the string.  Also, the new entries
251 * would have been ignored by old versions of CVS anyhow since those versions
252 * didn't know how to parse a port number.
253 *
254 * The "A" before "encoded_password" is a literal capital A.  It's a
255 * version number indicating which form of scrambling we're doing on
256 * the password -- someday we might provide something more secure than
257 * the trivial encoding we do now, and when that day comes, it would
258 * be nice to remain backward-compatible.
259 *
260 * Like .netrc, the file's permissions are the only thing preventing
261 * it from being read by others.  Unlike .netrc, we will not be
262 * fascist about it, at most issuing a warning, and never refusing to
263 * work.
264 *
265 * INPUTS
266 * 	operation	operation to perform
267 * 	root		cvsroot_t to look up
268 * 	newpassword	prescrambled new password, for password_entry_add_mode
269 *
270 * RETURNS
271 * 	-1	if password_entry_lookup_mode not specified
272 * 	NULL	on failed lookup
273 * 	pointer to a copy of the password string otherwise, which the caller is
274 * 		responsible for disposing of
275 */
276
277typedef enum password_entry_operation_e {
278    password_entry_lookup,
279    password_entry_delete,
280    password_entry_add
281} password_entry_operation_t;
282
283static char *
284password_entry_operation (operation, root, newpassword)
285    password_entry_operation_t operation;
286    cvsroot_t *root;
287    char *newpassword;
288{
289    char *passfile;
290    FILE *fp;
291    char *cvsroot_canonical = NULL;
292    char *password = NULL;
293    int line_length;
294    long line = -1;
295    char *linebuf = NULL;
296    size_t linebuf_len;
297    char *p;
298    int save_errno = 0;
299
300    if (root->method != pserver_method)
301    {
302	error (0, 0, "\
303internal error: can only call password_entry_operation with pserver method");
304	error (1, 0, "CVSROOT: %s", root->original);
305    }
306
307    cvsroot_canonical = normalize_cvsroot (root);
308
309    /* Yes, the method below reads the user's password file twice when we have
310     * to delete an entry.  It's inefficient, but we're not talking about a gig of
311     * data here.
312     */
313
314    passfile = construct_cvspass_filename ();
315    fp = CVS_FOPEN (passfile, "r");
316    if (fp == NULL)
317    {
318	error (0, errno, "warning: failed to open %s for reading", passfile);
319	goto process;
320    }
321
322    /* Check each line to see if we have this entry already. */
323    line = 0;
324    while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0)
325    {
326	line++;
327	password = password_entry_parseline (cvsroot_canonical, 1, line,
328                                             linebuf);
329	if (password != NULL)
330	    /* this is it!  break out and deal with linebuf */
331	    break;
332    }
333    if (line_length < 0 && !feof (fp))
334    {
335	error (0, errno, "cannot read %s", passfile);
336	goto error_exit;
337    }
338    if (fclose (fp) < 0)
339	/* not fatal, unless it cascades */
340	error (0, errno, "cannot close %s", passfile);
341    fp = NULL;
342
343    /* Utter, total, raving paranoia, I know. */
344    chmod (passfile, 0600);
345
346    /* a copy to return or keep around so we can reuse linebuf */
347    if (password != NULL)
348    {
349	/* chomp the EOL */
350	p = strchr (password, '\n');
351	if (p != NULL)
352	    *p = '\0';
353	password = xstrdup (password);
354    }
355
356process:
357
358    /* might as well return now */
359    if (operation == password_entry_lookup)
360	goto out;
361
362    /* same here */
363    if (operation == password_entry_delete && password == NULL)
364    {
365	error (0, 0, "Entry not found.");
366	goto out;
367    }
368
369    /* okay, file errors can simply be fatal from now on since we don't do
370     * anything else if we're in lookup mode
371     */
372
373    /* copy the file with the entry deleted unless we're in add
374     * mode and the line we found contains the same password we're supposed to
375     * add
376     */
377    if (!noexec && password != NULL && (operation == password_entry_delete
378        || (operation == password_entry_add
379            && strcmp (password, newpassword))))
380    {
381	long found_at = line;
382	char *tmp_name;
383	FILE *tmp_fp;
384
385	/* open the original file again */
386	fp = CVS_FOPEN (passfile, "r");
387	if (fp == NULL)
388	    error (1, errno, "failed to open %s for reading", passfile);
389
390	/* create and open a temp file */
391	if ((tmp_fp = cvs_temp_file (&tmp_name)) == NULL)
392	    error (1, errno, "unable to open temp file %s",
393		   tmp_name ? tmp_name : "(null)");
394
395	line = 0;
396	while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0)
397	{
398	    line++;
399	    if (line < found_at
400		|| (line != found_at
401		    && !password_entry_parseline (cvsroot_canonical, 0, line,
402                                                  linebuf)))
403	    {
404		if (fprintf (tmp_fp, "%s", linebuf) == EOF)
405		{
406		    /* try and clean up anyhow */
407		    error (0, errno, "fatal error: cannot write %s", tmp_name);
408		    if (fclose (tmp_fp) == EOF)
409			error (0, errno, "cannot close %s", tmp_name);
410		    /* call CVS_UNLINK instead of unlink_file since the file
411		     * got created in noexec mode
412		     */
413		    if (CVS_UNLINK (tmp_name) < 0)
414			error (0, errno, "cannot remove %s", tmp_name);
415		    /* but quit so we don't remove all the entries from a
416		     * user's password file accidentally
417		     */
418		    error (1, 0, "exiting");
419		}
420	    }
421	}
422	if (line_length < 0 && !feof (fp))
423	{
424	    error (0, errno, "cannot read %s", passfile);
425	    goto error_exit;
426	}
427	if (fclose (fp) < 0)
428	    /* not fatal, unless it cascades */
429	    error (0, errno, "cannot close %s", passfile);
430	if (fclose (tmp_fp) < 0)
431	    /* not fatal, unless it cascades */
432	    /* FIXME - does copy_file return correct results if the file wasn't
433	     * closed? should this be fatal?
434	     */
435	    error (0, errno, "cannot close %s", tmp_name);
436
437	/* FIXME: rename_file would make more sense (e.g. almost
438	 * always faster).
439	 *
440	 * I don't think so, unless we change the way rename_file works to
441	 * attempt a cp/rm sequence when rename fails since rename doesn't
442	 * work across file systems and it isn't uncommon to have /tmp
443	 * on its own partition.
444	 *
445	 * For that matter, it's probably not uncommon to have a home
446	 * directory on an NFS mount.
447	 */
448	copy_file (tmp_name, passfile);
449	if (CVS_UNLINK (tmp_name) < 0)
450	    error (0, errno, "cannot remove %s", tmp_name);
451	free (tmp_name);
452    }
453
454    /* in add mode, if we didn't find an entry or found an entry with a
455     * different password, append the new line
456     */
457    if (!noexec && operation == password_entry_add
458	    && (password == NULL || strcmp (password, newpassword)))
459    {
460	if ((fp = CVS_FOPEN (passfile, "a")) == NULL)
461	    error (1, errno, "could not open %s for writing", passfile);
462
463	if (fprintf (fp, "/1 %s %s\n", cvsroot_canonical, newpassword) == EOF)
464	    error (1, errno, "cannot write %s", passfile);
465	if (fclose (fp) < 0)
466	    error (1, errno, "cannot close %s", passfile);
467    }
468
469    /* Utter, total, raving paranoia, I know. */
470    chmod (passfile, 0600);
471
472    if (password)
473    {
474	free (password);
475	password = NULL;
476    }
477    if (linebuf)
478	free (linebuf);
479
480out:
481    free (cvsroot_canonical);
482    free (passfile);
483    return password;
484
485error_exit:
486    /* just exit when we're not in lookup mode */
487    if (operation != password_entry_lookup)
488	error (1, 0, "fatal error: exiting");
489    /* clean up and exit in lookup mode so we can try a login with a NULL
490     * password anyhow in case that's what we would have found
491     */
492    save_errno = errno;
493    if (fp != NULL)
494    {
495	/* Utter, total, raving paranoia, I know. */
496	chmod (passfile, 0600);
497	if(fclose (fp) < 0)
498	    error (0, errno, "cannot close %s", passfile);
499    }
500    if (linebuf)
501	free (linebuf);
502    if (cvsroot_canonical)
503	free (cvsroot_canonical);
504    free (passfile);
505    errno = save_errno;
506    return NULL;
507}
508
509
510
511/* Prompt for a password, and store it in the file "CVS/.cvspass".
512 */
513
514static const char *const login_usage[] =
515{
516    "Usage: %s %s\n",
517    "(Specify the --help global option for a list of other help options)\n",
518    NULL
519};
520
521int
522login (argc, argv)
523    int argc;
524    char **argv;
525{
526    char *typed_password;
527    char *cvsroot_canonical;
528
529    if (argc < 0)
530	usage (login_usage);
531
532    if (current_parsed_root->method != pserver_method)
533    {
534	error (0, 0, "can only use `login' command with the 'pserver' method");
535	error (1, 0, "CVSROOT: %s", current_parsed_root->original);
536    }
537
538    cvsroot_canonical = normalize_cvsroot(current_parsed_root);
539    printf ("Logging in to %s\n", cvsroot_canonical);
540    fflush (stdout);
541
542    if (current_parsed_root->password)
543    {
544	typed_password = scramble (current_parsed_root->password);
545    }
546    else
547    {
548	char *tmp;
549	tmp = getpass ("CVS password: ");
550	/* Must deal with a NULL return value here.  I haven't managed to
551	 * disconnect the CVS process from the tty and force a NULL return
552	 * in sanity.sh, but the Linux version of getpass is documented
553	 * to return NULL when it can't open /dev/tty...
554	 */
555	if (!tmp) error (1, errno, "login: Failed to read password.");
556	typed_password = scramble (tmp);
557	memset (tmp, 0, strlen (tmp));
558    }
559
560    /* Force get_cvs_password() to use this one (when the client
561     * confirms the new password with the server), instead of
562     * consulting the file.  We make a new copy because cvs_password
563     * will get zeroed by connect_to_server().  */
564    cvs_password = xstrdup (typed_password);
565
566    connect_to_pserver (current_parsed_root, NULL, NULL, 1, 0);
567
568    password_entry_operation (password_entry_add, current_parsed_root,
569                              typed_password);
570
571    free_cvs_password (typed_password);
572    free (cvsroot_canonical);
573
574    return 0;
575}
576
577
578
579/* Free the password returned by get_cvs_password() and also free the
580 * saved cvs_password if they are different pointers. Be paranoid
581 * about the in-memory copy of the password and overwrite it with zero
582 * bytes before doing the free().
583 */
584void
585free_cvs_password (char *password)
586{
587    if (password && password != cvs_password)
588    {
589	memset (password, 0, strlen (password));
590	free (password);
591    }
592
593    if (cvs_password)
594    {
595	memset (cvs_password, 0, strlen (cvs_password));
596	free (cvs_password);
597	cvs_password = NULL;
598    }
599}
600
601/* Returns the _scrambled_ password in freshly allocated memory.  The server
602 * must descramble before hashing and comparing.  If password file not found,
603 * or password not found in the file, just return NULL.
604 */
605char *
606get_cvs_password ()
607{
608    if (current_parsed_root->password)
609	return scramble (current_parsed_root->password);
610
611    /* If someone (i.e., login()) is calling connect_to_pserver() out of
612       context, then assume they have supplied the correct, scrambled
613       password. */
614    if (cvs_password)
615	return xstrdup (cvs_password);
616
617    if (getenv ("CVS_PASSWORD") != NULL)
618    {
619	/* In previous versions of CVS one could specify a password in
620	 * CVS_PASSWORD.  This is a bad idea, because in BSD variants
621	 * of unix anyone can see the environment variable with 'ps'.
622	 * But for users who were using that feature we want to at
623	 * least let them know what is going on.  After printing this
624	 * warning, we should fall through to the regular error where
625	 * we tell them to run "cvs login" (unless they already ran
626	 * it, of course).
627	 */
628	 error (0, 0, "CVS_PASSWORD is no longer supported; ignored");
629    }
630
631    if (current_parsed_root->method != pserver_method)
632    {
633	error (0, 0, "can only call get_cvs_password with pserver method");
634	error (1, 0, "CVSROOT: %s", current_parsed_root->original);
635    }
636
637    return password_entry_operation (password_entry_lookup,
638                                     current_parsed_root, NULL);
639}
640
641
642
643static const char *const logout_usage[] =
644{
645    "Usage: %s %s\n",
646    "(Specify the --help global option for a list of other help options)\n",
647    NULL
648};
649
650/* Remove any entry for the CVSRoot repository found in .cvspass. */
651int
652logout (argc, argv)
653    int argc;
654    char **argv;
655{
656    char *cvsroot_canonical;
657
658    if (argc < 0)
659	usage (logout_usage);
660
661    if (current_parsed_root->method != pserver_method)
662    {
663	error (0, 0, "can only use pserver method with `logout' command");
664	error (1, 0, "CVSROOT: %s", current_parsed_root->original);
665    }
666
667    /* Hmm.  Do we want a variant of this command which deletes _all_
668       the entries from the current .cvspass?  Might be easier to
669       remember than "rm ~/.cvspass" but then again if people are
670       mucking with HOME (common in Win95 as the system doesn't set
671       it), then this variant of "cvs logout" might give a false sense
672       of security, in that it wouldn't delete entries from any
673       .cvspass files but the current one.  */
674
675    if (!quiet)
676    {
677	cvsroot_canonical = normalize_cvsroot(current_parsed_root);
678	printf ("Logging out of %s\n", cvsroot_canonical);
679	fflush (stdout);
680	free (cvsroot_canonical);
681    }
682
683    password_entry_operation (password_entry_delete, current_parsed_root, NULL);
684
685    return 0;
686}
687
688#endif /* AUTH_CLIENT_SUPPORT from beginning of file. */
689