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