2007-10-13 Marco Ciampa <ciampix@libero.it>
[midnight-commander.git] / vfs / mcserv.c
blobd79c6f3554e04b63194190d3f59e965d3b54e5a4
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.
24 TODO:
25 opendir instead of keeping its table of file handles could return
26 the pointer and expect the client to send a proper value back each
27 time :-)
29 We should use syslog to register login/logout.
33 /* {{{ Includes and global variables */
35 #include <config.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #ifdef HAVE_UNISTD_H
40 # include <unistd.h>
41 #endif
42 #include <string.h>
43 #ifdef HAVE_LIMITS_H
44 # include <limits.h>
45 #endif
46 #ifndef NGROUPS_MAX
47 # include <sys/param.h>
48 # ifdef NGROUPS
49 # define NGROUPS_MAX NGROUPS
50 # endif
51 #endif
52 #ifdef HAVE_GRP_H
53 # include <grp.h>
54 #endif
55 #include <sys/types.h>
56 #include <sys/stat.h>
57 #include <sys/wait.h>
58 #include <errno.h>
59 #include <signal.h>
60 #ifdef HAVE_GETOPT_H
61 # include <getopt.h>
62 #endif
64 /* Network include files */
65 #include <sys/socket.h>
66 #include <netinet/in.h>
67 #include <netdb.h>
68 #ifdef HAVE_ARPA_INET_H
69 #include <arpa/inet.h>
70 #endif
71 #ifdef HAVE_PMAP_SET
72 # include <rpc/rpc.h>
73 # include <rpc/pmap_prot.h>
74 # ifdef HAVE_RPC_PMAP_CLNT_H
75 # include <rpc/pmap_clnt.h>
76 # endif
77 #endif
79 #if defined(HAVE_PAM)
80 # if !defined(HAVE_SECURITY_PAM_MISC_H)
81 # undef HAVE_PAM
82 # endif
83 #endif
85 /* Authentication include files */
86 #include <pwd.h>
87 #ifdef HAVE_PAM
88 # include <security/pam_misc.h>
89 # ifndef PAM_ESTABLISH_CRED
90 # define PAM_ESTABLISH_CRED PAM_CRED_ESTABLISH
91 # endif
92 #else
93 #endif /* !HAVE_PAM */
95 #ifdef HAVE_CRYPT_H
96 # include <crypt.h>
97 #endif /* !HAVE_CRYPT_H */
99 #ifdef HAVE_SHADOW_H
100 # include <shadow.h>
101 #else
102 # ifdef HAVE_SHADOW_SHADOW_H
103 # include <shadow/shadow.h>
104 # endif
105 #endif
108 * GNU gettext defines printf to libintl_printf on platforms that lack
109 * a native printf(3) capable of all POSIX features.
111 #undef ENABLE_NLS
112 #include "../src/global.h"
114 #include "../src/tty.h" /* enable/disable interrupt key */
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 time)
303 if (clnt_version == 1) {
304 char *ct;
305 int month;
307 ct = ctime (&time);
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 (msock, 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) time;
339 char buf[BUF_SMALL];
341 snprintf (buf, sizeof (buf), "%lx", ltime);
342 rpc_send (msock, 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 buffer[2048];
599 char *file;
600 int n;
602 rpc_get (msock, RPC_STRING, &file, RPC_END);
603 n = readlink (file, buffer, 2048 - 1);
604 send_status (n, errno);
605 if (n >= 0) {
606 buffer[n] = 0;
607 rpc_send (msock, RPC_STRING, buffer, 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 socket)
824 char c;
826 while (1) {
827 if (read (socket, &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 msock)
1054 return 0;
1057 static void
1058 login_reply (int logged_in)
1060 rpc_send (msock, RPC_INT, logged_in ? MC_LOGINOK : MC_INVALID_PASS,
1061 RPC_END);
1064 /* FIXME: Implement the anonymous login */
1065 static void
1066 do_login (void)
1068 char *username;
1069 char *password;
1070 int result;
1072 rpc_get (msock, RPC_LIMITED_STRING, &up_dir, RPC_LIMITED_STRING,
1073 &username, RPC_END);
1074 if (verbose)
1075 printf ("username: %s\n", username);
1077 if (r_auth) {
1078 logged_in = do_rauth (msock);
1079 if (logged_in) {
1080 login_reply (logged_in);
1081 return;
1084 rpc_send (msock, RPC_INT, MC_NEED_PASSWORD, RPC_END);
1085 rpc_get (msock, RPC_INT, &result, RPC_END);
1086 if (result == MC_QUIT)
1087 DO_QUIT_VOID ();
1088 if (result != MC_PASS) {
1089 if (verbose)
1090 printf ("do_login: Unknown response: %d\n", result);
1091 DO_QUIT_VOID ();
1093 rpc_get (msock, RPC_LIMITED_STRING, &password, RPC_END);
1094 logged_in = do_auth (username, password);
1095 endpwent ();
1096 login_reply (logged_in);
1099 /* }}} */
1101 /* {{{ Server and dispatching functions */
1103 /* This structure must be kept in synch with mcfs.h enums */
1105 static const struct _command {
1106 const char *command;
1107 void (*callback) (void);
1108 } commands[] = {
1110 "open", do_open}, {
1111 "close", do_close}, {
1112 "read", do_read}, {
1113 "write", do_write}, {
1114 "opendir", do_opendir}, {
1115 "readdir", do_readdir}, {
1116 "closedir", do_closedir}, {
1117 "stat ", do_stat}, {
1118 "lstat ", do_lstat}, {
1119 "fstat", do_fstat}, {
1120 "chmod", do_chmod}, {
1121 "chown", do_chown}, {
1122 "readlink ", do_readlink}, {
1123 "unlink", do_unlink}, {
1124 "rename", do_rename}, {
1125 "chdir ", do_chdir}, {
1126 "lseek", do_lseek}, {
1127 "rmdir", do_rmdir}, {
1128 "symlink", do_symlink}, {
1129 "mknod", do_mknod}, {
1130 "mkdir", do_mkdir}, {
1131 "link", do_link}, {
1132 "gethome", do_gethome}, {
1133 "getupdir", do_getupdir}, {
1134 "login", do_login}, {
1135 "quit", do_quit}, {
1136 "utime", do_utime}};
1138 static int ncommands = sizeof (commands) / sizeof (struct _command);
1140 static void
1141 exec_command (int command)
1143 if (command < 0 || command >= ncommands
1144 || commands[command].command == 0) {
1145 fprintf (stderr, "Got unknown command: %d\n", command);
1146 DO_QUIT_VOID ();
1148 if (verbose)
1149 printf ("Command: %s\n", commands[command].command);
1150 (*commands[command].callback) ();
1153 static void
1154 check_version (void)
1156 int version;
1158 rpc_get (msock, RPC_INT, &version, RPC_END);
1159 if (version >= 1 && version <= RPC_PROGVER)
1160 rpc_send (msock, RPC_INT, MC_VERSION_OK, RPC_END);
1161 else
1162 rpc_send (msock, RPC_INT, MC_VERSION_MISMATCH, RPC_END);
1164 clnt_version = version;
1167 /* This routine is called by rpc_get/rpc_send when the connection is closed */
1168 void
1169 tcp_invalidate_socket (int sock)
1171 if (verbose)
1172 printf ("Connection closed\n");
1173 DO_QUIT_VOID ();
1176 static void
1177 server (int sock)
1179 int command;
1181 msock = sock;
1182 quit_server = 0;
1184 check_version ();
1185 do {
1186 if (rpc_get (sock, RPC_INT, &command, RPC_END)
1187 && (logged_in || command == MC_LOGIN))
1188 exec_command (command);
1189 } while (!quit_server);
1192 /* }}} */
1194 /* {{{ Net support code */
1196 static const char *
1197 get_client (int portnum)
1199 int sock, newsocket;
1200 unsigned int clilen;
1201 struct sockaddr_in client_address, server_address;
1202 int yes = 1;
1204 if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
1205 return "Cannot create socket";
1207 /* Use this to debug: */
1208 if (setsockopt
1209 (sock, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof (yes)) < 0)
1210 return "setsockopt failed";
1212 memset ((char *) &server_address, 0, sizeof (server_address));
1213 server_address.sin_family = AF_INET;
1214 server_address.sin_addr.s_addr = htonl (INADDR_ANY);
1215 server_address.sin_port = htons (portnum);
1217 if (bind
1218 (sock, (struct sockaddr *) &server_address,
1219 sizeof (server_address)) < 0)
1220 return "Cannot bind";
1222 listen (sock, 5);
1224 for (;;) {
1225 int child;
1227 clilen = sizeof (client_address);
1228 newsocket =
1229 accept (sock, (struct sockaddr *) &client_address, &clilen);
1231 if (isDaemon && (child = fork ())) {
1232 int status;
1234 close (newsocket);
1235 waitpid (child, &status, 0);
1236 continue;
1239 if (isDaemon && fork ())
1240 exit (0);
1242 server (newsocket);
1243 close (newsocket);
1244 return 0;
1248 #ifdef HAVE_PMAP_SET
1249 static void
1250 signal_int_handler (int sig)
1252 (void) sig;
1254 pmap_unset (RPC_PROGNUM, RPC_PROGVER);
1256 #endif
1258 #ifndef IPPORT_RESERVED
1259 #define IPPORT_RESERVED 1024
1260 #endif
1262 static int
1263 get_port_number (void)
1265 int port = 0;
1267 #ifdef HAVE_RRESVPORT
1268 int start_port = IPPORT_RESERVED;
1270 port = rresvport (&start_port);
1271 if (port == -1) {
1272 if (geteuid () == 0) {
1273 fprintf (stderr,
1274 "Cannot bind the server on a reserved port\n");
1275 DO_QUIT_NONVOID (-1);
1277 port = 0;
1279 #endif
1280 if (port)
1281 return port;
1283 port = mcserver_port;
1285 return port;
1288 static void
1289 register_port (int portnum, int abort_if_fail)
1291 #ifdef HAVE_PMAP_SET
1292 /* Register our service with the portmapper */
1293 /* protocol: pmap_set (prognum, versnum, protocol, portp) */
1295 if (pmap_set (RPC_PROGNUM, RPC_PROGVER, IPPROTO_TCP, portnum))
1296 signal (SIGINT, signal_int_handler);
1297 else {
1298 fprintf (stderr, "Cannot register service with portmapper\n");
1299 if (abort_if_fail)
1300 exit (1);
1302 #else
1303 if (abort_if_fail) {
1304 fprintf (stderr,
1305 "This system lacks port registration, try using the -p\n"
1306 "flag to force installation at a given port");
1308 #endif
1311 /* }}} */
1314 main (int argc, char *argv[])
1316 const char *result;
1317 int c;
1319 while ((c = getopt (argc, argv, "fdiqp:v")) != -1) {
1320 switch (c) {
1321 case 'd':
1322 isDaemon = 1;
1323 verbose = 0;
1324 break;
1326 case 'v':
1327 verbose = 1;
1328 break;
1330 case 'f':
1331 ftp = 1;
1332 break;
1334 case 'q':
1335 verbose = 0;
1336 break;
1338 case 'p':
1339 portnum = atoi (optarg);
1340 break;
1342 case 'i':
1343 inetd_started = 1;
1344 break;
1346 case 'r':
1347 r_auth = 1;
1348 break;
1350 default:
1351 fprintf (stderr,
1352 "Usage is: mcserv [options] [-p portnum]\n\n"
1353 "options are:\n" "-d become a daemon (sets -q)\n"
1354 "-q quiet mode\n"
1355 /* "-r use rhost based authentication\n" */
1356 #ifndef HAVE_PAM
1357 "-f force ftp authentication\n"
1358 #endif
1359 "-v verbose mode\n"
1360 "-p to specify a port number to listen\n");
1361 exit (0);
1366 if (isDaemon && fork ())
1367 exit (0);
1369 if (portnum == 0)
1370 portnum = get_port_number ();
1372 if (portnum != -1) {
1373 register_port (portnum, 0);
1374 if (verbose)
1375 printf ("Using port %d\n", portnum);
1376 if ((result = get_client (portnum)))
1377 perror (result);
1378 #ifdef HAVE_PMAP_SET
1379 if (!isDaemon)
1380 pmap_unset (RPC_PROGNUM, RPC_PROGVER);
1381 #endif
1383 exit (return_code);
1386 /* FIXME: This function should not be used in mcserv */
1387 void
1388 vfs_die (const char *m)
1390 fputs (m, stderr);
1391 exit (1);