Fixed incorrect showing name of search library in summary screen
[midnight-commander.git] / vfs / mcserv.c
blob85b5b1eba44c044c725dbdcc7d6f6d1847ae2be6
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 == NULL) {
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 else {
240 send_status (n, 0);
241 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 g_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 g_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 (setuid (this->pw_uid))
1011 return 0;
1013 /* If the setuid call failed, then deny access */
1014 /* This should fix the problem on those machines with strange setups */
1015 if (getuid () != this->pw_uid)
1016 return 0;
1018 if (strcmp (username, "ftp") == 0)
1019 chroot (this->pw_dir);
1021 endpwent ();
1022 return auth;
1025 #if 0
1026 static int
1027 do_rauth (int socket)
1029 struct sockaddr_in from;
1030 struct hostent *hp;
1032 if (getpeername (0, (struct sockaddr *) &from, &fromlen) < 0)
1033 return 0;
1034 from.sin_port = ntohs ((unsigned short) from.sin_port);
1036 /* Strange, this should not happend */
1037 if (from.sin_family != AF_INET)
1038 return 0;
1040 hp = gethostbyaddr ((char *) &fromp.sin_addr, sizeof (struct in_addr),
1041 fromp.sin_family);
1044 #endif
1046 static int
1047 do_rauth (int sock)
1049 sock = 0; /* prevent warning */
1050 return 0;
1053 static void
1054 login_reply (int _logged_in)
1056 rpc_send (msock, RPC_INT, _logged_in ? MC_LOGINOK : MC_INVALID_PASS,
1057 RPC_END);
1060 /* FIXME: Implement the anonymous login */
1061 static void
1062 do_login (void)
1064 char *username;
1065 char *password;
1066 int result;
1068 rpc_get (msock, RPC_LIMITED_STRING, &up_dir, RPC_LIMITED_STRING,
1069 &username, RPC_END);
1070 if (verbose)
1071 printf ("username: %s\n", username);
1073 if (r_auth) {
1074 logged_in = do_rauth (msock);
1075 if (logged_in) {
1076 login_reply (logged_in);
1077 return;
1080 rpc_send (msock, RPC_INT, MC_NEED_PASSWORD, RPC_END);
1081 rpc_get (msock, RPC_INT, &result, RPC_END);
1082 if (result == MC_QUIT)
1083 DO_QUIT_VOID ();
1084 if (result != MC_PASS) {
1085 if (verbose)
1086 printf ("do_login: Unknown response: %d\n", result);
1087 DO_QUIT_VOID ();
1089 rpc_get (msock, RPC_LIMITED_STRING, &password, RPC_END);
1090 logged_in = do_auth (username, password);
1091 endpwent ();
1092 login_reply (logged_in);
1095 /* }}} */
1097 /* {{{ Server and dispatching functions */
1099 /* This structure must be kept in synch with mcfs.h enums */
1101 static const struct _command {
1102 const char *command;
1103 void (*callback) (void);
1104 } commands[] = {
1106 "open", do_open}, {
1107 "close", do_close}, {
1108 "read", do_read}, {
1109 "write", do_write}, {
1110 "opendir", do_opendir}, {
1111 "readdir", do_readdir}, {
1112 "closedir", do_closedir}, {
1113 "stat ", do_stat}, {
1114 "lstat ", do_lstat}, {
1115 "fstat", do_fstat}, {
1116 "chmod", do_chmod}, {
1117 "chown", do_chown}, {
1118 "readlink ", do_readlink}, {
1119 "unlink", do_unlink}, {
1120 "rename", do_rename}, {
1121 "chdir ", do_chdir}, {
1122 "lseek", do_lseek}, {
1123 "rmdir", do_rmdir}, {
1124 "symlink", do_symlink}, {
1125 "mknod", do_mknod}, {
1126 "mkdir", do_mkdir}, {
1127 "link", do_link}, {
1128 "gethome", do_gethome}, {
1129 "getupdir", do_getupdir}, {
1130 "login", do_login}, {
1131 "quit", do_quit}, {
1132 "utime", do_utime}};
1134 static int ncommands = sizeof (commands) / sizeof (struct _command);
1136 static void
1137 exec_command (int command)
1139 if (command < 0 || command >= ncommands
1140 || commands[command].command == 0) {
1141 fprintf (stderr, "Got unknown command: %d\n", command);
1142 DO_QUIT_VOID ();
1144 if (verbose)
1145 printf ("Command: %s\n", commands[command].command);
1146 (*commands[command].callback) ();
1149 static void
1150 check_version (void)
1152 int version;
1154 rpc_get (msock, RPC_INT, &version, RPC_END);
1155 if (version >= 1 && version <= RPC_PROGVER)
1156 rpc_send (msock, RPC_INT, MC_VERSION_OK, RPC_END);
1157 else
1158 rpc_send (msock, RPC_INT, MC_VERSION_MISMATCH, RPC_END);
1160 clnt_version = version;
1163 /* This routine is called by rpc_get/rpc_send when the connection is closed */
1164 void
1165 tcp_invalidate_socket (int sock)
1167 if (verbose)
1168 printf ("Connection closed [socket %d]\n", sock);
1169 DO_QUIT_VOID ();
1172 static void
1173 server (int sock)
1175 int command;
1177 msock = sock;
1178 quit_server = 0;
1180 check_version ();
1181 do {
1182 if (rpc_get (sock, RPC_INT, &command, RPC_END)
1183 && (logged_in || command == MC_LOGIN))
1184 exec_command (command);
1185 } while (!quit_server);
1188 /* }}} */
1190 /* {{{ Net support code */
1192 static const char *
1193 get_client (int port)
1195 int sock, newsocket;
1196 unsigned int clilen;
1197 struct sockaddr_in client_address, server_address;
1198 int yes = 1;
1200 if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
1201 return "Cannot create socket";
1203 /* Use this to debug: */
1204 if (setsockopt
1205 (sock, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof (yes)) < 0)
1206 return "setsockopt failed";
1208 memset ((char *) &server_address, 0, sizeof (server_address));
1209 server_address.sin_family = AF_INET;
1210 server_address.sin_addr.s_addr = htonl (INADDR_ANY);
1211 server_address.sin_port = htons (port);
1213 if (bind
1214 (sock, (struct sockaddr *) &server_address,
1215 sizeof (server_address)) < 0)
1216 return "Cannot bind";
1218 listen (sock, 5);
1220 for (;;) {
1221 int child;
1223 clilen = sizeof (client_address);
1224 newsocket =
1225 accept (sock, (struct sockaddr *) &client_address, &clilen);
1227 if (isDaemon && (child = fork ())) {
1228 int status;
1230 close (newsocket);
1231 waitpid (child, &status, 0);
1232 continue;
1235 if (isDaemon && fork ())
1236 exit (0);
1238 server (newsocket);
1239 close (newsocket);
1240 return 0;
1244 #ifdef HAVE_PMAP_SET
1245 static void
1246 signal_int_handler (int sig)
1248 (void) sig;
1250 pmap_unset (RPC_PROGNUM, RPC_PROGVER);
1252 #endif
1254 #ifndef IPPORT_RESERVED
1255 #define IPPORT_RESERVED 1024
1256 #endif
1258 static int
1259 get_port_number (void)
1261 int port = 0;
1263 #ifdef HAVE_RRESVPORT
1264 int start_port = IPPORT_RESERVED;
1266 port = rresvport (&start_port);
1267 if (port == -1) {
1268 if (geteuid () == 0) {
1269 fprintf (stderr,
1270 "Cannot bind the server on a reserved port\n");
1271 DO_QUIT_NONVOID (-1);
1273 port = 0;
1275 #endif
1276 if (port)
1277 return port;
1279 port = mcserver_port;
1281 return port;
1284 static void
1285 register_port (int port, int abort_if_fail)
1287 #ifdef HAVE_PMAP_SET
1288 /* Register our service with the portmapper */
1289 /* protocol: pmap_set (prognum, versnum, protocol, portp) */
1291 if (pmap_set (RPC_PROGNUM, RPC_PROGVER, IPPROTO_TCP, port))
1292 signal (SIGINT, signal_int_handler);
1293 else {
1294 fprintf (stderr, "Cannot register service with portmapper\n");
1295 if (abort_if_fail)
1296 exit (1);
1298 #else
1299 if (abort_if_fail) {
1300 fprintf (stderr,
1301 "This system lacks port registration, try using the -p\n"
1302 "flag to force installation at a given port");
1304 #endif
1307 /* }}} */
1310 main (int argc, char *argv[])
1312 const char *result;
1313 int c;
1315 while ((c = getopt (argc, argv, "fdiqp:v")) != -1) {
1316 switch (c) {
1317 case 'd':
1318 isDaemon = 1;
1319 verbose = 0;
1320 break;
1322 case 'v':
1323 verbose = 1;
1324 break;
1326 case 'f':
1327 ftp = 1;
1328 break;
1330 case 'q':
1331 verbose = 0;
1332 break;
1334 case 'p':
1335 portnum = atoi (optarg);
1336 break;
1338 case 'i':
1339 inetd_started = 1;
1340 break;
1342 case 'r':
1343 r_auth = 1;
1344 break;
1346 default:
1347 fprintf (stderr,
1348 "Usage is: mcserv [options] [-p portnum]\n\n"
1349 "options are:\n" "-d become a daemon (sets -q)\n"
1350 "-q quiet mode\n"
1351 /* "-r use rhost based authentication\n" */
1352 #ifndef HAVE_PAM
1353 "-f force ftp authentication\n"
1354 #endif
1355 "-v verbose mode\n"
1356 "-p to specify a port number to listen\n");
1357 exit (0);
1362 if (isDaemon && fork ())
1363 exit (0);
1365 if (portnum == 0)
1366 portnum = get_port_number ();
1368 if (portnum != -1) {
1369 register_port (portnum, 0);
1370 if (verbose)
1371 printf ("Using port %d\n", portnum);
1372 if ((result = get_client (portnum)))
1373 perror (result);
1374 #ifdef HAVE_PMAP_SET
1375 if (!isDaemon)
1376 pmap_unset (RPC_PROGNUM, RPC_PROGVER);
1377 #endif
1379 exit (return_code);
1382 /* FIXME: This function should not be used in mcserv */
1383 void
1384 vfs_die (const char *m)
1386 fputs (m, stderr);
1387 exit (1);