Merge branch '1858_segfault_in_search'
[midnight-commander.git] / vfs / mcserv.c
blob5fedc5554d0d76634597494f285c52549242cf92
1 /* Server for the Midnight Commander Virtual File System.
3 Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
4 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
6 Written by:
7 Miguel de Icaza, 1995, 1997,
8 Andrej Borsenkow 1996.
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 /**
26 * \file
27 * \brief Source: server for the Midnight Commander Virtual File System
28 * \author Miguel de Icaza
29 * \author Andrej Borsenkow
30 * \date 1995, 1996, 1997
32 * \todo opendir instead of keeping its table of file handles could return
33 * the pointer and expect the client to send a proper value back each
34 * time :-) We should use syslog to register login/logout.
37 /* {{{ Includes and global variables */
39 #include <config.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <string.h>
45 #include <fcntl.h>
46 #ifdef HAVE_LIMITS_H
47 # include <limits.h>
48 #endif
49 #ifndef NGROUPS_MAX
50 # include <sys/param.h>
51 # ifdef NGROUPS
52 # define NGROUPS_MAX NGROUPS
53 # endif
54 #endif
55 #include <grp.h>
56 #include <sys/types.h>
57 #include <sys/stat.h>
58 #include <sys/wait.h>
59 #include <errno.h>
60 #include <signal.h>
61 #ifdef HAVE_GETOPT_H
62 # include <getopt.h>
63 #endif
65 /* Network include files */
66 #include <sys/socket.h>
67 #include <netinet/in.h>
68 #include <netdb.h>
69 #ifdef HAVE_ARPA_INET_H
70 #include <arpa/inet.h>
71 #endif
72 #ifdef HAVE_PMAP_SET
73 # include <rpc/rpc.h>
74 # include <rpc/pmap_prot.h>
75 # ifdef HAVE_RPC_PMAP_CLNT_H
76 # include <rpc/pmap_clnt.h>
77 # endif
78 #endif
80 #if defined(HAVE_PAM)
81 # if !defined(HAVE_SECURITY_PAM_MISC_H)
82 # undef HAVE_PAM
83 # endif
84 #endif
86 /* Authentication include files */
87 #include <pwd.h>
88 #ifdef HAVE_PAM
89 # include <security/pam_misc.h>
90 # ifndef PAM_ESTABLISH_CRED
91 # define PAM_ESTABLISH_CRED PAM_CRED_ESTABLISH
92 # endif
93 #else
94 #endif /* !HAVE_PAM */
96 #ifdef HAVE_CRYPT_H
97 # include <crypt.h>
98 #endif /* !HAVE_CRYPT_H */
100 #ifdef HAVE_SHADOW_H
101 # include <shadow.h>
102 #else
103 # ifdef HAVE_SHADOW_SHADOW_H
104 # include <shadow/shadow.h>
105 # endif
106 #endif
109 * GNU gettext defines printf to libintl_printf on platforms that lack
110 * a native printf(3) capable of all POSIX features.
112 #undef ENABLE_NLS
113 #include "../src/global.h"
115 #include "../src/wtools.h" /* message() */
116 #include "../src/main.h" /* print_vfs_message */
117 #include "utilvfs.h"
118 #include "vfs.h"
119 #include "mcfs.h"
120 #include "mcfsutil.h"
121 #include "tcputil.h"
123 #ifndef INADDR_NONE
124 # define INADDR_NONE (0xffffffffU)
125 #endif
127 /* replacement for g_free() from glib */
128 #undef g_free
129 #define g_free(x) do {if (x) free (x);} while (0)
131 /* We don't care about SIGPIPE */
132 int got_sigpipe = 0;
134 /* The socket from which we accept commands */
135 int msock;
137 /* Requested version number from client */
138 static int clnt_version;
140 /* If non zero, we accept further commands */
141 int logged_in = 0;
143 /* Home directory */
144 const char *home_dir = NULL;
146 char *up_dir = NULL;
148 /* Were we started from inetd? */
149 int inetd_started = 0;
151 /* Are we running as a daemon? */
152 int isDaemon = 0;
154 /* guess */
155 int verbose = 0;
157 /* ftp auth */
158 int ftp = 0;
160 /* port number in which we listen to connections,
161 * if zero, we try to contact the portmapper to get a port, and
162 * if it's not possible, then we use a hardcoded value
164 int portnum = 0;
166 /* if the server will use rcmd based authentication (hosts.equiv .rhosts) */
167 int r_auth = 0;
169 #define OPENDIR_HANDLES 8
171 #define DO_QUIT_VOID() \
172 do { \
173 quit_server = 1; \
174 return_code = 1; \
175 return; \
176 } while (0)
178 /* Only used by get_port_number */
179 #define DO_QUIT_NONVOID(a) \
180 do { \
181 quit_server = 1; \
182 return_code = 1; \
183 return (a); \
184 } while (0)
186 char buffer[4096];
187 int debug = 1;
188 static int quit_server;
189 static int return_code;
191 /* }}} */
193 /* {{{ Misc routines */
195 static void
196 send_status (int status, int errno_number)
198 rpc_send (msock, RPC_INT, status, RPC_INT, errno_number, RPC_END);
199 errno = 0;
202 /* }}} */
204 /* {{{ File with handle operations */
206 static void
207 do_open (void)
209 int handle, flags, mode;
210 char *arg;
212 rpc_get (msock, RPC_STRING, &arg, RPC_INT, &flags, RPC_INT, &mode,
213 RPC_END);
215 handle = open (arg, flags, mode);
216 send_status (handle, errno);
217 g_free (arg);
220 static void
221 do_read (void)
223 int handle, count, n;
224 void *data;
226 rpc_get (msock, RPC_INT, &handle, RPC_INT, &count, RPC_END);
227 data = malloc (count);
228 if (!data) {
229 send_status (-1, ENOMEM);
230 return;
232 if (verbose)
233 printf ("count=%d\n", count);
234 n = read (handle, data, count);
235 if (verbose)
236 printf ("result=%d\n", n);
237 if (n < 0) {
238 send_status (-1, errno);
239 return;
241 send_status (n, 0);
242 rpc_send (msock, RPC_BLOCK, n, data, RPC_END);
244 g_free (data);
247 static void
248 do_write (void)
250 int handle, count, status, written = 0;
251 char buf[8192];
253 rpc_get (msock, RPC_INT, &handle, RPC_INT, &count, RPC_END);
254 status = 0;
255 while (count) {
256 int nbytes = count > 8192 ? 8192 : count;
258 rpc_get (msock, RPC_BLOCK, nbytes, buf, RPC_END);
259 status = write (handle, buf, nbytes);
260 if (status < 0) {
261 send_status (status, errno);
262 return;
264 /* FIXED: amount written must be returned to caller */
265 written += status;
266 if (status < nbytes) {
267 send_status (written, errno);
268 return;
270 count -= nbytes;
272 send_status (written, errno);
275 static void
276 do_lseek (void)
278 int handle, offset, whence, status;
280 rpc_get (msock, RPC_INT, &handle, RPC_INT, &offset, RPC_INT, &whence,
281 RPC_END);
282 status = lseek (handle, offset, whence);
283 send_status (status, errno);
286 static void
287 do_close (void)
289 int handle, status;
291 rpc_get (msock, RPC_INT, &handle, RPC_END);
292 status = close (handle);
293 send_status (status, errno);
296 /* }}} */
298 /* {{{ Stat family routines */
300 static void
301 send_time (int sock, time_t t)
303 if (clnt_version == 1) {
304 char *ct;
305 int month;
307 ct = ctime (&t);
308 ct[3] = ct[10] = ct[13] = ct[16] = ct[19] = 0;
310 /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
311 if (ct[4] == 'J') {
312 if (ct[5] == 'a') {
313 month = 0;
314 } else
315 month = (ct[6] == 'n') ? 5 : 6;
316 } else if (ct[4] == 'F') {
317 month = 1;
318 } else if (ct[4] == 'M') {
319 month = (ct[6] == 'r') ? 2 : 5;
320 } else if (ct[4] == 'A') {
321 month = (ct[5] == 'p') ? 3 : 7;
322 } else if (ct[4] == 'S') {
323 month = 8;
324 } else if (ct[4] == 'O') {
325 month = 9;
326 } else if (ct[4] == 'N') {
327 month = 10;
328 } else
329 month = 11;
330 rpc_send (sock, RPC_INT, atoi (&ct[17]), /* sec */
331 RPC_INT, atoi (&ct[14]), /* min */
332 RPC_INT, atoi (&ct[11]), /* hour */
333 RPC_INT, atoi (&ct[8]), /* mday */
334 RPC_INT, atoi (&ct[20]), /* year */
335 RPC_INT, month, /* month */
336 RPC_END);
337 } else {
338 long ltime = (long) t;
339 char buf[BUF_SMALL];
341 snprintf (buf, sizeof (buf), "%lx", ltime);
342 rpc_send (sock, RPC_STRING, buf, RPC_END);
346 static void
347 send_stat_info (struct stat *st)
349 long mylong;
350 int blocks =
351 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
352 st->st_blocks;
353 #else
354 st->st_size / 1024;
355 #endif
357 #ifdef HAVE_STRUCT_STAT_ST_RDEV
358 mylong = st->st_rdev;
359 #else
360 mylong = 0;
361 #endif
362 rpc_send (msock, RPC_INT, (long) mylong, RPC_INT, (long) st->st_ino,
363 RPC_INT, (long) st->st_mode, RPC_INT, (long) st->st_nlink,
364 RPC_INT, (long) st->st_uid, RPC_INT, (long) st->st_gid,
365 RPC_INT, (long) st->st_size, RPC_INT, (long) blocks,
366 RPC_END);
367 send_time (msock, st->st_atime);
368 send_time (msock, st->st_mtime);
369 send_time (msock, st->st_ctime);
372 static void
373 do_lstat (void)
375 struct stat st;
376 char *file;
377 int n;
379 rpc_get (msock, RPC_STRING, &file, RPC_END);
380 n = lstat (file, &st);
381 send_status (n, errno);
382 if (n >= 0)
383 send_stat_info (&st);
384 g_free (file);
387 static void
388 do_fstat (void)
390 int handle;
391 int n;
392 struct stat st;
394 rpc_get (msock, RPC_INT, &handle, RPC_END);
395 n = fstat (handle, &st);
396 send_status (n, errno);
397 if (n < 0)
398 return;
400 send_stat_info (&st);
403 static void
404 do_stat (void)
406 struct stat st;
407 int n;
408 char *file;
410 rpc_get (msock, RPC_STRING, &file, RPC_END);
412 n = stat (file, &st);
413 send_status (n, errno);
414 if (n >= 0)
415 send_stat_info (&st);
416 g_free (file);
419 /* }}} */
421 /* {{{ Directory lookup operations */
423 static struct {
424 int used;
425 DIR *dirs[OPENDIR_HANDLES];
426 char *names[OPENDIR_HANDLES];
427 } mcfs_DIR;
429 static void
430 close_handle (int handle)
432 if (mcfs_DIR.used > 0)
433 mcfs_DIR.used--;
434 if (mcfs_DIR.dirs[handle])
435 closedir (mcfs_DIR.dirs[handle]);
436 g_free (mcfs_DIR.names[handle]);
437 mcfs_DIR.dirs[handle] = 0;
438 mcfs_DIR.names[handle] = 0;
441 static void
442 do_opendir (void)
444 int handle, i;
445 char *arg;
446 DIR *p;
448 rpc_get (msock, RPC_STRING, &arg, RPC_END);
450 if (mcfs_DIR.used == OPENDIR_HANDLES) {
451 send_status (-1, ENFILE); /* Error */
452 g_free (arg);
453 return;
456 handle = -1;
457 for (i = 0; i < OPENDIR_HANDLES; i++) {
458 if (mcfs_DIR.dirs[i] == 0) {
459 handle = i;
460 break;
464 if (handle == -1) {
465 send_status (-1, EMFILE);
466 g_free (arg);
467 if (!inetd_started)
468 fprintf (stderr,
469 "OOPS! you have found a bug in mc - do_opendir()!\n");
470 return;
473 if (verbose)
474 printf ("handle=%d\n", handle);
475 p = opendir (arg);
476 if (p) {
477 mcfs_DIR.dirs[handle] = p;
478 mcfs_DIR.names[handle] = arg;
479 mcfs_DIR.used++;
481 /* Because 0 is an error value */
482 rpc_send (msock, RPC_INT, handle + 1, RPC_INT, 0, RPC_END);
484 } else {
485 send_status (-1, errno);
486 g_free (arg);
490 /* Sends the complete directory listing, as well as the stat information */
491 static void
492 do_readdir (void)
494 struct dirent *dirent;
495 struct stat st;
496 int handle, n;
498 rpc_get (msock, RPC_INT, &handle, RPC_END);
500 if (!handle) {
501 rpc_send (msock, RPC_INT, 0, RPC_END);
502 return;
505 /* We incremented it in opendir */
506 handle--;
508 while ((dirent = readdir (mcfs_DIR.dirs[handle]))) {
509 int fname_len;
510 char *fname;
511 int length = NLENGTH (dirent);
513 rpc_send (msock, RPC_INT, length, RPC_END);
514 rpc_send (msock, RPC_BLOCK, length, dirent->d_name, RPC_END);
515 fname_len =
516 strlen (mcfs_DIR.names[handle]) + strlen (dirent->d_name) + 2;
517 fname = malloc (fname_len);
518 snprintf (fname, fname_len, "%s/%s", mcfs_DIR.names[handle],
519 dirent->d_name);
520 n = lstat (fname, &st);
521 g_free (fname);
522 send_status (n, errno);
523 if (n >= 0)
524 send_stat_info (&st);
526 rpc_send (msock, RPC_INT, 0, RPC_END);
529 static void
530 do_closedir (void)
532 int handle;
534 rpc_get (msock, RPC_INT, &handle, RPC_END);
535 close_handle (handle - 1);
538 /* }}} */
540 /* {{{ Operations with one and two file name argument */
542 static void
543 do_chdir (void)
545 char *file;
546 int status;
548 rpc_get (msock, RPC_STRING, &file, RPC_END);
550 status = chdir (file);
551 send_status (status, errno);
552 g_free (file);
555 static void
556 do_rmdir (void)
558 char *file;
559 int status;
561 rpc_get (msock, RPC_STRING, &file, RPC_END);
563 status = rmdir (file);
564 send_status (status, errno);
565 g_free (file);
568 static void
569 do_mkdir (void)
571 char *file;
572 int mode, status;
574 rpc_get (msock, RPC_STRING, &file, RPC_INT, &mode, RPC_END);
576 status = mkdir (file, mode);
577 send_status (status, errno);
578 g_free (file);
581 static void
582 do_mknod (void)
584 char *file;
585 int mode, dev, status;
587 rpc_get (msock, RPC_STRING, &file, RPC_INT, &mode, RPC_INT, &dev,
588 RPC_END);
590 status = mknod (file, mode, dev);
591 send_status (status, errno);
592 g_free (file);
595 static void
596 do_readlink (void)
598 char buf[2048];
599 char *file;
600 int n;
602 rpc_get (msock, RPC_STRING, &file, RPC_END);
603 n = readlink (file, buf, 2048 - 1);
604 send_status (n, errno);
605 if (n >= 0) {
606 buf[n] = 0;
607 rpc_send (msock, RPC_STRING, buf, RPC_END);
609 g_free (file);
612 static void
613 do_unlink (void)
615 char *file;
616 int status;
618 rpc_get (msock, RPC_STRING, &file, RPC_END);
619 status = unlink (file);
620 send_status (status, errno);
621 g_free (file);
624 static void
625 do_rename (void)
627 char *f1, *f2;
628 int status;
630 rpc_get (msock, RPC_STRING, &f1, RPC_STRING, &f2, RPC_END);
631 status = rename (f1, f2);
632 send_status (status, errno);
633 g_free (f1);
634 g_free (f2);
637 static void
638 do_symlink (void)
640 char *f1, *f2;
641 int status;
643 rpc_get (msock, RPC_STRING, &f1, RPC_STRING, &f2, RPC_END);
644 status = symlink (f1, f2);
645 send_status (status, errno);
646 g_free (f1);
647 g_free (f2);
650 static void
651 do_link (void)
653 char *f1, *f2;
654 int status;
656 rpc_get (msock, RPC_STRING, &f1, RPC_STRING, &f2, RPC_END);
657 status = link (f1, f2);
658 send_status (status, errno);
659 g_free (f1);
660 g_free (f2);
664 /* }}} */
666 /* {{{ Misc commands */
668 static void
669 do_gethome (void)
671 rpc_send (msock, RPC_STRING, (home_dir) ? home_dir : "/", RPC_END);
674 static void
675 do_getupdir (void)
677 rpc_send (msock, RPC_STRING, (up_dir) ? up_dir : "/", RPC_END);
680 static void
681 do_chmod (void)
683 char *file;
684 int mode, status;
686 rpc_get (msock, RPC_STRING, &file, RPC_INT, &mode, RPC_END);
687 status = chmod (file, mode);
688 send_status (status, errno);
689 g_free (file);
692 static void
693 do_chown (void)
695 char *file;
696 int owner, group, status;
698 rpc_get (msock, RPC_STRING, &file, RPC_INT, &owner, RPC_INT, &group,
699 RPC_END);
700 status = chown (file, owner, group);
701 send_status (status, errno);
702 g_free (file);
705 static void
706 do_utime (void)
708 char *file;
709 int status;
710 long atime;
711 long mtime;
712 char *as;
713 char *ms;
714 struct utimbuf times;
716 rpc_get (msock, RPC_STRING, &file, RPC_STRING, &as, RPC_STRING, &ms,
717 RPC_END);
718 sscanf (as, "%lx", &atime);
719 sscanf (ms, "%lx", &mtime);
720 if (verbose)
721 printf ("Got a = %s, m = %s, comp a = %ld, m = %ld\n", as, ms,
722 atime, mtime);
723 g_free (as);
724 g_free (ms);
725 times.actime = (time_t) atime;
726 times.modtime = (time_t) mtime;
727 status = utime (file, &times);
728 send_status (status, errno);
729 g_free (file);
732 static void
733 do_quit (void)
735 quit_server = 1;
738 #ifdef HAVE_PAM
740 struct user_pass {
741 const char *username;
742 const char *password;
745 static int
746 mc_pam_conversation (int messages, const struct pam_message **msg,
747 struct pam_response **resp, void *appdata_ptr)
749 struct pam_response *r;
750 struct user_pass *up = appdata_ptr;
751 int status;
753 r = (struct pam_response *) malloc (sizeof (struct pam_response) *
754 messages);
755 if (!r)
756 return PAM_CONV_ERR;
757 *resp = r;
759 for (status = PAM_SUCCESS; messages--; msg++, r++) {
760 switch ((*msg)->msg_style) {
762 case PAM_PROMPT_ECHO_ON:
763 r->resp = strdup (up->username);
764 r->resp_retcode = PAM_SUCCESS;
765 break;
767 case PAM_PROMPT_ECHO_OFF:
768 r->resp = strdup (up->password);
769 r->resp_retcode = PAM_SUCCESS;
770 break;
772 case PAM_ERROR_MSG:
773 r->resp = NULL;
774 r->resp_retcode = PAM_SUCCESS;
775 break;
777 case PAM_TEXT_INFO:
778 r->resp = NULL;
779 r->resp_retcode = PAM_SUCCESS;
780 break;
783 return status;
786 static struct pam_conv conv = { &mc_pam_conversation, NULL };
789 /* Return 0 if authentication failed, 1 otherwise */
790 static int
791 mc_pam_auth (const char *username, const char *password)
793 pam_handle_t *pamh;
794 struct user_pass up;
795 int status;
797 up.username = username;
798 up.password = password;
799 conv.appdata_ptr = &up;
801 if ((status =
802 pam_start ("mcserv", username, &conv, &pamh)) != PAM_SUCCESS)
803 goto failed_pam;
804 if ((status = pam_authenticate (pamh, 0)) != PAM_SUCCESS)
805 goto failed_pam;
806 if ((status = pam_acct_mgmt (pamh, 0)) != PAM_SUCCESS)
807 goto failed_pam;
808 if ((status = pam_setcred (pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
809 goto failed_pam;
810 pam_end (pamh, status);
811 return 0;
813 failed_pam:
814 pam_end (pamh, status);
815 return 1;
818 #else /* !HAVE_PAM */
820 /* Keep reading until we find a \n */
821 static int
822 next_line (int sock)
824 char c;
826 while (1) {
827 if (read (sock, &c, 1) <= 0)
828 return 0;
829 if (c == '\n')
830 return 1;
834 static int
835 ftp_answer (int sock, const char *text)
837 char answer[4];
839 next_line (sock);
840 socket_read_block (sock, answer, 3);
841 answer[3] = 0;
842 if (strcmp (answer, text) == 0)
843 return 1;
844 return 0;
847 static int
848 send_string (int sock, const char *string)
850 return socket_write_block (sock, string, strlen (string));
853 static int
854 do_ftp_auth (const char *username, const char *password)
856 struct sockaddr_in local_address;
857 unsigned long inaddr;
858 int my_socket;
859 char answer[4];
861 memset ((char *) &local_address, 0, sizeof (local_address));
863 local_address.sin_family = AF_INET;
864 /* FIXME: extract the ftp port with the proper function */
865 local_address.sin_port = htons (21);
867 /* Convert localhost to usable format */
868 if ((inaddr = inet_addr ("127.0.0.1")) != INADDR_NONE)
869 memcpy ((char *) &local_address.sin_addr, (char *) &inaddr,
870 sizeof (inaddr));
872 if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
873 if (!isDaemon)
874 fprintf (stderr, "do_auth: can't create socket\n");
875 return 0;
877 if (connect
878 (my_socket, (struct sockaddr *) &local_address,
879 sizeof (local_address)) < 0) {
880 fprintf (stderr,
881 "do_auth: can't connect to ftp daemon for authentication\n");
882 close (my_socket);
883 return 0;
885 send_string (my_socket, "user ");
886 send_string (my_socket, username);
887 send_string (my_socket, "\r\n");
888 if (!ftp_answer (my_socket, "331")) {
889 send_string (my_socket, "quit\r\n");
890 close (my_socket);
891 return 0;
893 next_line (my_socket); /* Eat all the line */
894 send_string (my_socket, "pass ");
895 send_string (my_socket, password);
896 send_string (my_socket, "\r\n");
897 socket_read_block (my_socket, answer, 3);
898 answer[3] = 0;
899 send_string (my_socket, "\r\n");
900 send_string (my_socket, "quit\r\n");
901 close (my_socket);
902 if (strcmp (answer, "230") == 0)
903 return 1;
904 return 0;
907 #ifdef HAVE_CRYPT
908 static int
909 do_classic_auth (const char *username, const char *password)
911 int ret = 0;
912 const char *encr_pwd = NULL;
913 struct passwd *pw;
914 #ifdef HAVE_SHADOW
915 struct spwd *spw;
916 #endif
918 if ((pw = getpwnam (username)) == 0)
919 return 0;
921 #ifdef HAVE_SHADOW
922 setspent ();
924 /* Password expiration is not checked! */
925 if ((spw = getspnam (username)) == NULL)
926 encr_pwd = "*";
927 else
928 encr_pwd = spw->sp_pwdp;
930 endspent ();
931 #else
932 encr_pwd = pw->pw_passwd;
933 #endif
935 if (strcmp (crypt (password, encr_pwd), encr_pwd) == 0)
936 ret = 1;
938 endpwent ();
939 return ret;
941 #endif /* HAVE_CRYPT */
942 #endif /* !HAVE_PAM */
944 /* Try to authenticate the user based on:
945 - PAM if the system has it, else it checks:
946 - pwdauth if the system supports it.
947 - conventional auth (check salt on /etc/passwd, crypt, and compare
948 - try to contact the local ftp server and login (if -f flag used)
950 static int
951 do_auth (const char *username, const char *password)
953 int auth = 0;
954 struct passwd *this;
956 if (strcmp (username, "anonymous") == 0)
957 username = "ftp";
959 #ifdef HAVE_PAM
960 if (mc_pam_auth (username, password) == 0)
961 auth = 1;
962 #else /* if there is no pam */
963 #ifdef HAVE_PWDAUTH
964 if (pwdauth (username, password) == 0)
965 auth = 1;
966 else
967 #endif
968 #ifdef HAVE_CRYPT
969 if (do_classic_auth (username, password))
970 auth = 1;
971 else
972 #endif
973 if (ftp)
974 auth = do_ftp_auth (username, password);
975 #endif /* not pam */
977 if (!auth)
978 return 0;
980 this = getpwnam (username);
981 if (this == 0)
982 return 0;
984 if (chdir (this->pw_dir) == -1)
985 return 0;
987 if (this->pw_dir[strlen (this->pw_dir) - 1] == '/')
988 home_dir = strdup (this->pw_dir);
989 else {
990 char *new_home_dir = malloc (strlen (this->pw_dir) + 2);
991 if (new_home_dir) {
992 strcpy (new_home_dir, this->pw_dir);
993 strcat (new_home_dir, "/");
994 home_dir = new_home_dir;
995 } else
996 home_dir = "/";
1000 if (setgid (this->pw_gid) == -1)
1001 return 0;
1003 #ifdef HAVE_INITGROUPS
1004 #ifdef NGROUPS_MAX
1005 if (NGROUPS_MAX > 1 && initgroups (this->pw_name, this->pw_gid))
1006 return 0;
1007 #endif
1008 #endif
1010 #if defined (HAVE_SETUID)
1011 if (setuid (this->pw_uid))
1012 return 0;
1013 #elif defined (HAVE_SETREUID)
1014 if (setreuid (this->pw_uid, this->pw_uid))
1015 return 0;
1016 #endif
1018 /* If the setuid call failed, then deny access */
1019 /* This should fix the problem on those machines with strange setups */
1020 if (getuid () != this->pw_uid)
1021 return 0;
1023 if (strcmp (username, "ftp") == 0)
1024 chroot (this->pw_dir);
1026 endpwent ();
1027 return auth;
1030 #if 0
1031 static int
1032 do_rauth (int socket)
1034 struct sockaddr_in from;
1035 struct hostent *hp;
1037 if (getpeername (0, (struct sockaddr *) &from, &fromlen) < 0)
1038 return 0;
1039 from.sin_port = ntohs ((unsigned short) from.sin_port);
1041 /* Strange, this should not happend */
1042 if (from.sin_family != AF_INET)
1043 return 0;
1045 hp = gethostbyaddr ((char *) &fromp.sin_addr, sizeof (struct in_addr),
1046 fromp.sin_family);
1049 #endif
1051 static int
1052 do_rauth (int sock)
1054 sock = 0; /* prevent warning */
1055 return 0;
1058 static void
1059 login_reply (int _logged_in)
1061 rpc_send (msock, RPC_INT, _logged_in ? MC_LOGINOK : MC_INVALID_PASS,
1062 RPC_END);
1065 /* FIXME: Implement the anonymous login */
1066 static void
1067 do_login (void)
1069 char *username;
1070 char *password;
1071 int result;
1073 rpc_get (msock, RPC_LIMITED_STRING, &up_dir, RPC_LIMITED_STRING,
1074 &username, RPC_END);
1075 if (verbose)
1076 printf ("username: %s\n", username);
1078 if (r_auth) {
1079 logged_in = do_rauth (msock);
1080 if (logged_in) {
1081 login_reply (logged_in);
1082 return;
1085 rpc_send (msock, RPC_INT, MC_NEED_PASSWORD, RPC_END);
1086 rpc_get (msock, RPC_INT, &result, RPC_END);
1087 if (result == MC_QUIT)
1088 DO_QUIT_VOID ();
1089 if (result != MC_PASS) {
1090 if (verbose)
1091 printf ("do_login: Unknown response: %d\n", result);
1092 DO_QUIT_VOID ();
1094 rpc_get (msock, RPC_LIMITED_STRING, &password, RPC_END);
1095 logged_in = do_auth (username, password);
1096 endpwent ();
1097 login_reply (logged_in);
1100 /* }}} */
1102 /* {{{ Server and dispatching functions */
1104 /* This structure must be kept in synch with mcfs.h enums */
1106 static const struct _command {
1107 const char *command;
1108 void (*callback) (void);
1109 } commands[] = {
1111 "open", do_open}, {
1112 "close", do_close}, {
1113 "read", do_read}, {
1114 "write", do_write}, {
1115 "opendir", do_opendir}, {
1116 "readdir", do_readdir}, {
1117 "closedir", do_closedir}, {
1118 "stat ", do_stat}, {
1119 "lstat ", do_lstat}, {
1120 "fstat", do_fstat}, {
1121 "chmod", do_chmod}, {
1122 "chown", do_chown}, {
1123 "readlink ", do_readlink}, {
1124 "unlink", do_unlink}, {
1125 "rename", do_rename}, {
1126 "chdir ", do_chdir}, {
1127 "lseek", do_lseek}, {
1128 "rmdir", do_rmdir}, {
1129 "symlink", do_symlink}, {
1130 "mknod", do_mknod}, {
1131 "mkdir", do_mkdir}, {
1132 "link", do_link}, {
1133 "gethome", do_gethome}, {
1134 "getupdir", do_getupdir}, {
1135 "login", do_login}, {
1136 "quit", do_quit}, {
1137 "utime", do_utime}};
1139 static int ncommands = sizeof (commands) / sizeof (struct _command);
1141 static void
1142 exec_command (int command)
1144 if (command < 0 || command >= ncommands
1145 || commands[command].command == 0) {
1146 fprintf (stderr, "Got unknown command: %d\n", command);
1147 DO_QUIT_VOID ();
1149 if (verbose)
1150 printf ("Command: %s\n", commands[command].command);
1151 (*commands[command].callback) ();
1154 static void
1155 check_version (void)
1157 int version;
1159 rpc_get (msock, RPC_INT, &version, RPC_END);
1160 if (version >= 1 && version <= RPC_PROGVER)
1161 rpc_send (msock, RPC_INT, MC_VERSION_OK, RPC_END);
1162 else
1163 rpc_send (msock, RPC_INT, MC_VERSION_MISMATCH, RPC_END);
1165 clnt_version = version;
1168 /* This routine is called by rpc_get/rpc_send when the connection is closed */
1169 void
1170 tcp_invalidate_socket (int sock)
1172 if (verbose)
1173 printf ("Connection closed [socket %d]\n", sock);
1174 DO_QUIT_VOID ();
1177 static void
1178 server (int sock)
1180 int command;
1182 msock = sock;
1183 quit_server = 0;
1185 check_version ();
1186 do {
1187 if (rpc_get (sock, RPC_INT, &command, RPC_END)
1188 && (logged_in || command == MC_LOGIN))
1189 exec_command (command);
1190 } while (!quit_server);
1193 /* }}} */
1195 /* {{{ Net support code */
1197 static const char *
1198 get_client (int port)
1200 int sock, newsocket;
1201 unsigned int clilen;
1202 struct sockaddr_in client_address, server_address;
1203 int yes = 1;
1205 if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
1206 return "Cannot create socket";
1208 /* Use this to debug: */
1209 if (setsockopt
1210 (sock, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof (yes)) < 0)
1211 return "setsockopt failed";
1213 memset ((char *) &server_address, 0, sizeof (server_address));
1214 server_address.sin_family = AF_INET;
1215 server_address.sin_addr.s_addr = htonl (INADDR_ANY);
1216 server_address.sin_port = htons (port);
1218 if (bind
1219 (sock, (struct sockaddr *) &server_address,
1220 sizeof (server_address)) < 0)
1221 return "Cannot bind";
1223 listen (sock, 5);
1225 for (;;) {
1226 int child;
1228 clilen = sizeof (client_address);
1229 newsocket =
1230 accept (sock, (struct sockaddr *) &client_address, &clilen);
1232 if (isDaemon && (child = fork ())) {
1233 int status;
1235 close (newsocket);
1236 waitpid (child, &status, 0);
1237 continue;
1240 if (isDaemon && fork ())
1241 exit (0);
1243 server (newsocket);
1244 close (newsocket);
1245 return 0;
1249 #ifdef HAVE_PMAP_SET
1250 static void
1251 signal_int_handler (int sig)
1253 (void) sig;
1255 pmap_unset (RPC_PROGNUM, RPC_PROGVER);
1257 #endif
1259 #ifndef IPPORT_RESERVED
1260 #define IPPORT_RESERVED 1024
1261 #endif
1263 static int
1264 get_port_number (void)
1266 int port = 0;
1268 #ifdef HAVE_RRESVPORT
1269 int start_port = IPPORT_RESERVED;
1271 port = rresvport (&start_port);
1272 if (port == -1) {
1273 if (geteuid () == 0) {
1274 fprintf (stderr,
1275 "Cannot bind the server on a reserved port\n");
1276 DO_QUIT_NONVOID (-1);
1278 port = 0;
1280 #endif
1281 if (port)
1282 return port;
1284 port = mcserver_port;
1286 return port;
1289 static void
1290 register_port (int port, int abort_if_fail)
1292 #ifdef HAVE_PMAP_SET
1293 /* Register our service with the portmapper */
1294 /* protocol: pmap_set (prognum, versnum, protocol, portp) */
1296 if (pmap_set (RPC_PROGNUM, RPC_PROGVER, IPPROTO_TCP, port))
1297 signal (SIGINT, signal_int_handler);
1298 else {
1299 fprintf (stderr, "Cannot register service with portmapper\n");
1300 if (abort_if_fail)
1301 exit (1);
1303 #else
1304 if (abort_if_fail) {
1305 fprintf (stderr,
1306 "This system lacks port registration, try using the -p\n"
1307 "flag to force installation at a given port");
1309 #endif
1312 /* }}} */
1315 main (int argc, char *argv[])
1317 const char *result;
1318 int c;
1320 while ((c = getopt (argc, argv, "fdiqp:v")) != -1) {
1321 switch (c) {
1322 case 'd':
1323 isDaemon = 1;
1324 verbose = 0;
1325 break;
1327 case 'v':
1328 verbose = 1;
1329 break;
1331 case 'f':
1332 ftp = 1;
1333 break;
1335 case 'q':
1336 verbose = 0;
1337 break;
1339 case 'p':
1340 portnum = atoi (optarg);
1341 break;
1343 case 'i':
1344 inetd_started = 1;
1345 break;
1347 case 'r':
1348 r_auth = 1;
1349 break;
1351 default:
1352 fprintf (stderr,
1353 "Usage is: mcserv [options] [-p portnum]\n\n"
1354 "options are:\n" "-d become a daemon (sets -q)\n"
1355 "-q quiet mode\n"
1356 /* "-r use rhost based authentication\n" */
1357 #ifndef HAVE_PAM
1358 "-f force ftp authentication\n"
1359 #endif
1360 "-v verbose mode\n"
1361 "-p to specify a port number to listen\n");
1362 exit (0);
1367 if (isDaemon && fork ())
1368 exit (0);
1370 if (portnum == 0)
1371 portnum = get_port_number ();
1373 if (portnum != -1) {
1374 register_port (portnum, 0);
1375 if (verbose)
1376 printf ("Using port %d\n", portnum);
1377 if ((result = get_client (portnum)))
1378 perror (result);
1379 #ifdef HAVE_PMAP_SET
1380 if (!isDaemon)
1381 pmap_unset (RPC_PROGNUM, RPC_PROGVER);
1382 #endif
1384 exit (return_code);
1387 /* FIXME: This function should not be used in mcserv */
1388 void
1389 vfs_die (const char *m)
1391 fputs (m, stderr);
1392 exit (1);