Merge branch 'vendor/TNFTP'
[dragonfly.git] / contrib / cvs-1.12 / 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 /* 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. */
35 static char *cvs_password = NULL;
36
37 static char *construct_cvspass_filename (void);
38
39 /* The return value will need to be freed. */
40 static char *
41 construct_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  */
105 static char *
106 password_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
274 typedef enum password_entry_operation_e {
275     password_entry_lookup,
276     password_entry_delete,
277     password_entry_add
278 } password_entry_operation_t;
279
280 static char *
281 password_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, "\
297 internal 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
350 process:
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
473 out:
474     free (cvsroot_canonical);
475     free (passfile);
476     return password;
477
478 error_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
507 static 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
514 int
515 login (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     memset (typed_password, 0, strlen (typed_password));
563     free (typed_password);
564
565     free (cvs_password);
566     free (cvsroot_canonical);
567     cvs_password = NULL;
568
569     return 0;
570 }
571
572
573
574 /* Returns the _scrambled_ password.  The server must descramble
575    before hashing and comparing.  If password file not found, or
576    password not found in the file, just return NULL. */
577 char *
578 get_cvs_password (void)
579 {
580     if (current_parsed_root->password)
581         return scramble (current_parsed_root->password);
582  
583     /* If someone (i.e., login()) is calling connect_to_pserver() out of
584        context, then assume they have supplied the correct, scrambled
585        password. */
586     if (cvs_password)
587         return cvs_password;
588
589     if (getenv ("CVS_PASSWORD") != NULL)
590     {
591         /* In previous versions of CVS one could specify a password in
592          * CVS_PASSWORD.  This is a bad idea, because in BSD variants
593          * of unix anyone can see the environment variable with 'ps'.
594          * But for users who were using that feature we want to at
595          * least let them know what is going on.  After printing this
596          * warning, we should fall through to the regular error where
597          * we tell them to run "cvs login" (unless they already ran
598          * it, of course).
599          */
600          error (0, 0, "CVS_PASSWORD is no longer supported; ignored");
601     }
602
603     if (current_parsed_root->method != pserver_method)
604     {
605         error (0, 0, "can only call get_cvs_password with pserver method");
606         error (1, 0, "CVSROOT: %s", current_parsed_root->original);
607     }
608
609     return password_entry_operation (password_entry_lookup,
610                                      current_parsed_root, NULL);
611 }
612
613
614
615 static const char *const logout_usage[] =
616 {
617     "Usage: %s %s\n",
618     "(Specify the --help global option for a list of other help options)\n",
619     NULL
620 };
621
622 /* Remove any entry for the CVSRoot repository found in .cvspass. */
623 int
624 logout (int argc, char **argv)
625 {
626     char *cvsroot_canonical;
627
628     if (argc < 0)
629         usage (logout_usage);
630
631     if (current_parsed_root->method != pserver_method)
632     {
633         error (0, 0, "can only use pserver method with `logout' command");
634         error (1, 0, "CVSROOT: %s", current_parsed_root->original);
635     }
636
637     /* Hmm.  Do we want a variant of this command which deletes _all_
638        the entries from the current .cvspass?  Might be easier to
639        remember than "rm ~/.cvspass" but then again if people are
640        mucking with HOME (common in Win95 as the system doesn't set
641        it), then this variant of "cvs logout" might give a false sense
642        of security, in that it wouldn't delete entries from any
643        .cvspass files but the current one.  */
644
645     if (!quiet)
646     {
647         cvsroot_canonical = normalize_cvsroot(current_parsed_root);
648         printf ("Logging out of %s\n", cvsroot_canonical);
649         fflush (stdout);
650         free (cvsroot_canonical);
651     }
652
653     password_entry_operation (password_entry_delete, current_parsed_root, NULL);
654
655     return 0;
656 }
657
658 #endif /* AUTH_CLIENT_SUPPORT from beginning of file. */