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