Merge branch '54_dlg_mouse_handling'
[midnight-commander.git] / vfs / mcserv.c
blob4c4986adba16c794bec2e912fd31fa234987d500
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 #ifdef HAVE_LIMITS_H
46 # include <limits.h>
47 #endif
48 #ifndef NGROUPS_MAX
49 # include <sys/param.h>
50 # ifdef NGROUPS
51 # define NGROUPS_MAX NGROUPS
52 # endif
53 #endif
54 #include <grp.h>
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/wtools.h" /* message() */
115 #include "../src/main.h" /* print_vfs_message */
116 #include "utilvfs.h"
117 #include "vfs.h"
118 #include "mcfs.h"
119 #include "mcfsutil.h"
120 #include "tcputil.h"
122 #ifndef INADDR_NONE
123 # define INADDR_NONE (0xffffffffU)
124 #endif
126 /* replacement for g_free() from glib */
127 #undef g_free
128 #define g_free(x) do {if (x) free (x);} while (0)
130 /* We don't care about SIGPIPE */
131 int got_sigpipe = 0;
133 /* The socket from which we accept commands */
134 int msock;
136 /* Requested version number from client */
137 static int clnt_version;
139 /* If non zero, we accept further commands */
140 int logged_in = 0;
142 /* Home directory */
143 const char *home_dir = NULL;
145 char *up_dir = NULL;
147 /* Were we started from inetd? */
148 int inetd_started = 0;
150 /* Are we running as a daemon? */
151 int isDaemon = 0;
153 /* guess */
154 int verbose = 0;
156 /* ftp auth */
157 int ftp = 0;
159 /* port number in which we listen to connections,
160 * if zero, we try to contact the portmapper to get a port, and
161 * if it's not possible, then we use a hardcoded value
163 int portnum = 0;
165 /* if the server will use rcmd based authentication (hosts.equiv .rhosts) */
166 int r_auth = 0;
168 #define OPENDIR_HANDLES 8
170 #define DO_QUIT_VOID() \
171 do { \
172 quit_server = 1; \
173 return_code = 1; \
174 return; \
175 } while (0)
177 /* Only used by get_port_number */
178 #define DO_QUIT_NONVOID(a) \
179 do { \
180 quit_server = 1; \
181 return_code = 1; \
182 return (a); \
183 } while (0)
185 char buffer[4096];
186 int debug = 1;
187 static int quit_server;
188 static int return_code;
190 /* }}} */
192 /* {{{ Misc routines */
194 static void
195 send_status (int status, int errno_number)
197 rpc_send (msock, RPC_INT, status, RPC_INT, errno_number, RPC_END);
198 errno = 0;
201 /* }}} */
203 /* {{{ File with handle operations */
205 static void
206 do_open (void)
208 int handle, flags, mode;
209 char *arg;
211 rpc_get (msock, RPC_STRING, &arg, RPC_INT, &flags, RPC_INT, &mode,
212 RPC_END);
214 handle = open (arg, flags, mode);
215 send_status (handle, errno);
216 g_free (arg);
219 static void
220 do_read (void)
222 int handle, count, n;
223 void *data;
225 rpc_get (msock, RPC_INT, &handle, RPC_INT, &count, RPC_END);
226 data = malloc (count);
227 if (!data) {
228 send_status (-1, ENOMEM);
229 return;
231 if (verbose)
232 printf ("count=%d\n", count);
233 n = read (handle, data, count);
234 if (verbose)
235 printf ("result=%d\n", n);
236 if (n < 0) {
237 send_status (-1, errno);
238 return;
240 send_status (n, 0);
241 rpc_send (msock, RPC_BLOCK, n, data, RPC_END);
243 g_free (data);
246 static void
247 do_write (void)
249 int handle, count, status, written = 0;
250 char buf[8192];
252 rpc_get (msock, RPC_INT, &handle, RPC_INT, &count, RPC_END);
253 status = 0;
254 while (count) {
255 int nbytes = count > 8192 ? 8192 : count;
257 rpc_get (msock, RPC_BLOCK, nbytes, buf, RPC_END);
258 status = write (handle, buf, nbytes);
259 if (status < 0) {
260 send_status (status, errno);
261 return;
263 /* FIXED: amount written must be returned to caller */
264 written += status;
265 if (status < nbytes) {
266 send_status (written, errno);
267 return;
269 count -= nbytes;
271 send_status (written, errno);
274 static void
275 do_lseek (void)
277 int handle, offset, whence, status;
279 rpc_get (msock, RPC_INT, &handle, RPC_INT, &offset, RPC_INT, &whence,
280 RPC_END);
281 status = lseek (handle, offset, whence);
282 send_status (status, errno);
285 static void
286 do_close (void)
288 int handle, status;
290 rpc_get (msock, RPC_INT, &handle, RPC_END);
291 status = close (handle);
292 send_status (status, errno);
295 /* }}} */
297 /* {{{ Stat family routines */
299 static void
300 send_time (int sock, time_t time)
302 if (clnt_version == 1) {
303 char *ct;
304 int month;
306 ct = ctime (&time);
307 ct[3] = ct[10] = ct[13] = ct[16] = ct[19] = 0;
309 /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
310 if (ct[4] == 'J') {
311 if (ct[5] == 'a') {
312 month = 0;
313 } else
314 month = (ct[6] == 'n') ? 5 : 6;
315 } else if (ct[4] == 'F') {
316 month = 1;
317 } else if (ct[4] == 'M') {
318 month = (ct[6] == 'r') ? 2 : 5;
319 } else if (ct[4] == 'A') {
320 month = (ct[5] == 'p') ? 3 : 7;
321 } else if (ct[4] == 'S') {
322 month = 8;
323 } else if (ct[4] == 'O') {
324 month = 9;
325 } else if (ct[4] == 'N') {
326 month = 10;
327 } else
328 month = 11;
329 rpc_send (msock, RPC_INT, atoi (&ct[17]), /* sec */
330 RPC_INT, atoi (&ct[14]), /* min */
331 RPC_INT, atoi (&ct[11]), /* hour */
332 RPC_INT, atoi (&ct[8]), /* mday */
333 RPC_INT, atoi (&ct[20]), /* year */
334 RPC_INT, month, /* month */
335 RPC_END);
336 } else {
337 long ltime = (long) time;
338 char buf[BUF_SMALL];
340 snprintf (buf, sizeof (buf), "%lx", ltime);
341 rpc_send (msock, RPC_STRING, buf, RPC_END);
345 static void
346 send_stat_info (struct stat *st)
348 long mylong;
349 int blocks =
350 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
351 st->st_blocks;
352 #else
353 st->st_size / 1024;
354 #endif
356 #ifdef HAVE_STRUCT_STAT_ST_RDEV
357 mylong = st->st_rdev;
358 #else
359 mylong = 0;
360 #endif
361 rpc_send (msock, RPC_INT, (long) mylong, RPC_INT, (long) st->st_ino,
362 RPC_INT, (long) st->st_mode, RPC_INT, (long) st->st_nlink,
363 RPC_INT, (long) st->st_uid, RPC_INT, (long) st->st_gid,
364 RPC_INT, (long) st->st_size, RPC_INT, (long) blocks,
365 RPC_END);
366 send_time (msock, st->st_atime);
367 send_time (msock, st->st_mtime);
368 send_time (msock, st->st_ctime);
371 static void
372 do_lstat (void)
374 struct stat st;
375 char *file;
376 int n;
378 rpc_get (msock, RPC_STRING, &file, RPC_END);
379 n = lstat (file, &st);
380 send_status (n, errno);
381 if (n >= 0)
382 send_stat_info (&st);
383 g_free (file);
386 static void
387 do_fstat (void)
389 int handle;
390 int n;
391 struct stat st;
393 rpc_get (msock, RPC_INT, &handle, RPC_END);
394 n = fstat (handle, &st);
395 send_status (n, errno);
396 if (n < 0)
397 return;
399 send_stat_info (&st);
402 static void
403 do_stat (void)
405 struct stat st;
406 int n;
407 char *file;
409 rpc_get (msock, RPC_STRING, &file, RPC_END);
411 n = stat (file, &st);
412 send_status (n, errno);
413 if (n >= 0)
414 send_stat_info (&st);
415 g_free (file);
418 /* }}} */
420 /* {{{ Directory lookup operations */
422 static struct {
423 int used;
424 DIR *dirs[OPENDIR_HANDLES];
425 char *names[OPENDIR_HANDLES];
426 } mcfs_DIR;
428 static void
429 close_handle (int handle)
431 if (mcfs_DIR.used > 0)
432 mcfs_DIR.used--;
433 if (mcfs_DIR.dirs[handle])
434 closedir (mcfs_DIR.dirs[handle]);
435 g_free (mcfs_DIR.names[handle]);
436 mcfs_DIR.dirs[handle] = 0;
437 mcfs_DIR.names[handle] = 0;
440 static void
441 do_opendir (void)
443 int handle, i;
444 char *arg;
445 DIR *p;
447 rpc_get (msock, RPC_STRING, &arg, RPC_END);
449 if (mcfs_DIR.used == OPENDIR_HANDLES) {
450 send_status (-1, ENFILE); /* Error */
451 g_free (arg);
452 return;
455 handle = -1;
456 for (i = 0; i < OPENDIR_HANDLES; i++) {
457 if (mcfs_DIR.dirs[i] == 0) {
458 handle = i;
459 break;
463 if (handle == -1) {
464 send_status (-1, EMFILE);
465 g_free (arg);
466 if (!inetd_started)
467 fprintf (stderr,
468 "OOPS! you have found a bug in mc - do_opendir()!\n");
469 return;
472 if (verbose)
473 printf ("handle=%d\n", handle);
474 p = opendir (arg);
475 if (p) {
476 mcfs_DIR.dirs[handle] = p;
477 mcfs_DIR.names[handle] = arg;
478 mcfs_DIR.used++;
480 /* Because 0 is an error value */
481 rpc_send (msock, RPC_INT, handle + 1, RPC_INT, 0, RPC_END);
483 } else {
484 send_status (-1, errno);
485 g_free (arg);
489 /* Sends the complete directory listing, as well as the stat information */
490 static void
491 do_readdir (void)
493 struct dirent *dirent;
494 struct stat st;
495 int handle, n;
497 rpc_get (msock, RPC_INT, &handle, RPC_END);
499 if (!handle) {
500 rpc_send (msock, RPC_INT, 0, RPC_END);
501 return;
504 /* We incremented it in opendir */
505 handle--;
507 while ((dirent = readdir (mcfs_DIR.dirs[handle]))) {
508 int fname_len;
509 char *fname;
510 int length = NLENGTH (dirent);
512 rpc_send (msock, RPC_INT, length, RPC_END);
513 rpc_send (msock, RPC_BLOCK, length, dirent->d_name, RPC_END);
514 fname_len =
515 strlen (mcfs_DIR.names[handle]) + strlen (dirent->d_name) + 2;
516 fname = malloc (fname_len);
517 snprintf (fname, fname_len, "%s/%s", mcfs_DIR.names[handle],
518 dirent->d_name);
519 n = lstat (fname, &st);
520 g_free (fname);
521 send_status (n, errno);
522 if (n >= 0)
523 send_stat_info (&st);
525 rpc_send (msock, RPC_INT, 0, RPC_END);
528 static void
529 do_closedir (void)
531 int handle;
533 rpc_get (msock, RPC_INT, &handle, RPC_END);
534 close_handle (handle - 1);
537 /* }}} */
539 /* {{{ Operations with one and two file name argument */
541 static void
542 do_chdir (void)
544 char *file;
545 int status;
547 rpc_get (msock, RPC_STRING, &file, RPC_END);
549 status = chdir (file);
550 send_status (status, errno);
551 g_free (file);
554 static void
555 do_rmdir (void)
557 char *file;
558 int status;
560 rpc_get (msock, RPC_STRING, &file, RPC_END);
562 status = rmdir (file);
563 send_status (status, errno);
564 g_free (file);
567 static void
568 do_mkdir (void)
570 char *file;
571 int mode, status;
573 rpc_get (msock, RPC_STRING, &file, RPC_INT, &mode, RPC_END);
575 status = mkdir (file, mode);
576 send_status (status, errno);
577 g_free (file);
580 static void
581 do_mknod (void)
583 char *file;
584 int mode, dev, status;
586 rpc_get (msock, RPC_STRING, &file, RPC_INT, &mode, RPC_INT, &dev,
587 RPC_END);
589 status = mknod (file, mode, dev);
590 send_status (status, errno);
591 g_free (file);
594 static void
595 do_readlink (void)
597 char buffer[2048];
598 char *file;
599 int n;
601 rpc_get (msock, RPC_STRING, &file, RPC_END);
602 n = readlink (file, buffer, 2048 - 1);
603 send_status (n, errno);
604 if (n >= 0) {
605 buffer[n] = 0;
606 rpc_send (msock, RPC_STRING, buffer, RPC_END);
608 g_free (file);
611 static void
612 do_unlink (void)
614 char *file;
615 int status;
617 rpc_get (msock, RPC_STRING, &file, RPC_END);
618 status = unlink (file);
619 send_status (status, errno);
620 g_free (file);
623 static void
624 do_rename (void)
626 char *f1, *f2;
627 int status;
629 rpc_get (msock, RPC_STRING, &f1, RPC_STRING, &f2, RPC_END);
630 status = rename (f1, f2);
631 send_status (status, errno);
632 g_free (f1);
633 g_free (f2);
636 static void
637 do_symlink (void)
639 char *f1, *f2;
640 int status;
642 rpc_get (msock, RPC_STRING, &f1, RPC_STRING, &f2, RPC_END);
643 status = symlink (f1, f2);
644 send_status (status, errno);
645 g_free (f1);
646 g_free (f2);
649 static void
650 do_link (void)
652 char *f1, *f2;
653 int status;
655 rpc_get (msock, RPC_STRING, &f1, RPC_STRING, &f2, RPC_END);
656 status = link (f1, f2);
657 send_status (status, errno);
658 g_free (f1);
659 g_free (f2);
663 /* }}} */
665 /* {{{ Misc commands */
667 static void
668 do_gethome (void)
670 rpc_send (msock, RPC_STRING, (home_dir) ? home_dir : "/", RPC_END);
673 static void
674 do_getupdir (void)
676 rpc_send (msock, RPC_STRING, (up_dir) ? up_dir : "/", RPC_END);
679 static void
680 do_chmod (void)
682 char *file;
683 int mode, status;
685 rpc_get (msock, RPC_STRING, &file, RPC_INT, &mode, RPC_END);
686 status = chmod (file, mode);
687 send_status (status, errno);
688 g_free (file);
691 static void
692 do_chown (void)
694 char *file;
695 int owner, group, status;
697 rpc_get (msock, RPC_STRING, &file, RPC_INT, &owner, RPC_INT, &group,
698 RPC_END);
699 status = chown (file, owner, group);
700 send_status (status, errno);
701 g_free (file);
704 static void
705 do_utime (void)
707 char *file;
708 int status;
709 long atime;
710 long mtime;
711 char *as;
712 char *ms;
713 struct utimbuf times;
715 rpc_get (msock, RPC_STRING, &file, RPC_STRING, &as, RPC_STRING, &ms,
716 RPC_END);
717 sscanf (as, "%lx", &atime);
718 sscanf (ms, "%lx", &mtime);
719 if (verbose)
720 printf ("Got a = %s, m = %s, comp a = %ld, m = %ld\n", as, ms,
721 atime, mtime);
722 g_free (as);
723 g_free (ms);
724 times.actime = (time_t) atime;
725 times.modtime = (time_t) mtime;
726 status = utime (file, &times);
727 send_status (status, errno);
728 g_free (file);
731 static void
732 do_quit (void)
734 quit_server = 1;
737 #ifdef HAVE_PAM
739 struct user_pass {
740 const char *username;
741 const char *password;
744 static int
745 mc_pam_conversation (int messages, const struct pam_message **msg,
746 struct pam_response **resp, void *appdata_ptr)
748 struct pam_response *r;
749 struct user_pass *up = appdata_ptr;
750 int status;
752 r = (struct pam_response *) malloc (sizeof (struct pam_response) *
753 messages);
754 if (!r)
755 return PAM_CONV_ERR;
756 *resp = r;
758 for (status = PAM_SUCCESS; messages--; msg++, r++) {
759 switch ((*msg)->msg_style) {
761 case PAM_PROMPT_ECHO_ON:
762 r->resp = strdup (up->username);
763 r->resp_retcode = PAM_SUCCESS;
764 break;
766 case PAM_PROMPT_ECHO_OFF:
767 r->resp = strdup (up->password);
768 r->resp_retcode = PAM_SUCCESS;
769 break;
771 case PAM_ERROR_MSG:
772 r->resp = NULL;
773 r->resp_retcode = PAM_SUCCESS;
774 break;
776 case PAM_TEXT_INFO:
777 r->resp = NULL;
778 r->resp_retcode = PAM_SUCCESS;
779 break;
782 return status;
785 static struct pam_conv conv = { &mc_pam_conversation, NULL };
788 /* Return 0 if authentication failed, 1 otherwise */
789 static int
790 mc_pam_auth (const char *username, const char *password)
792 pam_handle_t *pamh;
793 struct user_pass up;
794 int status;
796 up.username = username;
797 up.password = password;
798 conv.appdata_ptr = &up;
800 if ((status =
801 pam_start ("mcserv", username, &conv, &pamh)) != PAM_SUCCESS)
802 goto failed_pam;
803 if ((status = pam_authenticate (pamh, 0)) != PAM_SUCCESS)
804 goto failed_pam;
805 if ((status = pam_acct_mgmt (pamh, 0)) != PAM_SUCCESS)
806 goto failed_pam;
807 if ((status = pam_setcred (pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
808 goto failed_pam;
809 pam_end (pamh, status);
810 return 0;
812 failed_pam:
813 pam_end (pamh, status);
814 return 1;
817 #else /* !HAVE_PAM */
819 /* Keep reading until we find a \n */
820 static int
821 next_line (int socket)
823 char c;
825 while (1) {
826 if (read (socket, &c, 1) <= 0)
827 return 0;
828 if (c == '\n')
829 return 1;
833 static int
834 ftp_answer (int sock, const char *text)
836 char answer[4];
838 next_line (sock);
839 socket_read_block (sock, answer, 3);
840 answer[3] = 0;
841 if (strcmp (answer, text) == 0)
842 return 1;
843 return 0;
846 static int
847 send_string (int sock, const char *string)
849 return socket_write_block (sock, string, strlen (string));
852 static int
853 do_ftp_auth (const char *username, const char *password)
855 struct sockaddr_in local_address;
856 unsigned long inaddr;
857 int my_socket;
858 char answer[4];
860 memset ((char *) &local_address, 0, sizeof (local_address));
862 local_address.sin_family = AF_INET;
863 /* FIXME: extract the ftp port with the proper function */
864 local_address.sin_port = htons (21);
866 /* Convert localhost to usable format */
867 if ((inaddr = inet_addr ("127.0.0.1")) != INADDR_NONE)
868 memcpy ((char *) &local_address.sin_addr, (char *) &inaddr,
869 sizeof (inaddr));
871 if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
872 if (!isDaemon)
873 fprintf (stderr, "do_auth: can't create socket\n");
874 return 0;
876 if (connect
877 (my_socket, (struct sockaddr *) &local_address,
878 sizeof (local_address)) < 0) {
879 fprintf (stderr,
880 "do_auth: can't connect to ftp daemon for authentication\n");
881 close (my_socket);
882 return 0;
884 send_string (my_socket, "user ");
885 send_string (my_socket, username);
886 send_string (my_socket, "\r\n");
887 if (!ftp_answer (my_socket, "331")) {
888 send_string (my_socket, "quit\r\n");
889 close (my_socket);
890 return 0;
892 next_line (my_socket); /* Eat all the line */
893 send_string (my_socket, "pass ");
894 send_string (my_socket, password);
895 send_string (my_socket, "\r\n");
896 socket_read_block (my_socket, answer, 3);
897 answer[3] = 0;
898 send_string (my_socket, "\r\n");
899 send_string (my_socket, "quit\r\n");
900 close (my_socket);
901 if (strcmp (answer, "230") == 0)
902 return 1;
903 return 0;
906 #ifdef HAVE_CRYPT
907 static int
908 do_classic_auth (const char *username, const char *password)
910 int ret = 0;
911 const char *encr_pwd = NULL;
912 struct passwd *pw;
913 #ifdef HAVE_SHADOW
914 struct spwd *spw;
915 #endif
917 if ((pw = getpwnam (username)) == 0)
918 return 0;
920 #ifdef HAVE_SHADOW
921 setspent ();
923 /* Password expiration is not checked! */
924 if ((spw = getspnam (username)) == NULL)
925 encr_pwd = "*";
926 else
927 encr_pwd = spw->sp_pwdp;
929 endspent ();
930 #else
931 encr_pwd = pw->pw_passwd;
932 #endif
934 if (strcmp (crypt (password, encr_pwd), encr_pwd) == 0)
935 ret = 1;
937 endpwent ();
938 return ret;
940 #endif /* HAVE_CRYPT */
941 #endif /* !HAVE_PAM */
943 /* Try to authenticate the user based on:
944 - PAM if the system has it, else it checks:
945 - pwdauth if the system supports it.
946 - conventional auth (check salt on /etc/passwd, crypt, and compare
947 - try to contact the local ftp server and login (if -f flag used)
949 static int
950 do_auth (const char *username, const char *password)
952 int auth = 0;
953 struct passwd *this;
955 if (strcmp (username, "anonymous") == 0)
956 username = "ftp";
958 #ifdef HAVE_PAM
959 if (mc_pam_auth (username, password) == 0)
960 auth = 1;
961 #else /* if there is no pam */
962 #ifdef HAVE_PWDAUTH
963 if (pwdauth (username, password) == 0)
964 auth = 1;
965 else
966 #endif
967 #ifdef HAVE_CRYPT
968 if (do_classic_auth (username, password))
969 auth = 1;
970 else
971 #endif
972 if (ftp)
973 auth = do_ftp_auth (username, password);
974 #endif /* not pam */
976 if (!auth)
977 return 0;
979 this = getpwnam (username);
980 if (this == 0)
981 return 0;
983 if (chdir (this->pw_dir) == -1)
984 return 0;
986 if (this->pw_dir[strlen (this->pw_dir) - 1] == '/')
987 home_dir = strdup (this->pw_dir);
988 else {
989 char *new_home_dir = malloc (strlen (this->pw_dir) + 2);
990 if (new_home_dir) {
991 strcpy (new_home_dir, this->pw_dir);
992 strcat (new_home_dir, "/");
993 home_dir = new_home_dir;
994 } else
995 home_dir = "/";
999 if (setgid (this->pw_gid) == -1)
1000 return 0;
1002 #ifdef HAVE_INITGROUPS
1003 #ifdef NGROUPS_MAX
1004 if (NGROUPS_MAX > 1 && initgroups (this->pw_name, this->pw_gid))
1005 return 0;
1006 #endif
1007 #endif
1009 #if defined (HAVE_SETUID)
1010 if (setuid (this->pw_uid))
1011 return 0;
1012 #elif defined (HAVE_SETREUID)
1013 if (setreuid (this->pw_uid, this->pw_uid))
1014 return 0;
1015 #endif
1017 /* If the setuid call failed, then deny access */
1018 /* This should fix the problem on those machines with strange setups */
1019 if (getuid () != this->pw_uid)
1020 return 0;
1022 if (strcmp (username, "ftp") == 0)
1023 chroot (this->pw_dir);
1025 endpwent ();
1026 return auth;
1029 #if 0
1030 static int
1031 do_rauth (int socket)
1033 struct sockaddr_in from;
1034 struct hostent *hp;
1036 if (getpeername (0, (struct sockaddr *) &from, &fromlen) < 0)
1037 return 0;
1038 from.sin_port = ntohs ((unsigned short) from.sin_port);
1040 /* Strange, this should not happend */
1041 if (from.sin_family != AF_INET)
1042 return 0;
1044 hp = gethostbyaddr ((char *) &fromp.sin_addr, sizeof (struct in_addr),
1045 fromp.sin_family);
1048 #endif
1050 static int
1051 do_rauth (int msock)
1053 return 0;
1056 static void
1057 login_reply (int logged_in)
1059 rpc_send (msock, RPC_INT, logged_in ? MC_LOGINOK : MC_INVALID_PASS,
1060 RPC_END);
1063 /* FIXME: Implement the anonymous login */
1064 static void
1065 do_login (void)
1067 char *username;
1068 char *password;
1069 int result;
1071 rpc_get (msock, RPC_LIMITED_STRING, &up_dir, RPC_LIMITED_STRING,
1072 &username, RPC_END);
1073 if (verbose)
1074 printf ("username: %s\n", username);
1076 if (r_auth) {
1077 logged_in = do_rauth (msock);
1078 if (logged_in) {
1079 login_reply (logged_in);
1080 return;
1083 rpc_send (msock, RPC_INT, MC_NEED_PASSWORD, RPC_END);
1084 rpc_get (msock, RPC_INT, &result, RPC_END);
1085 if (result == MC_QUIT)
1086 DO_QUIT_VOID ();
1087 if (result != MC_PASS) {
1088 if (verbose)
1089 printf ("do_login: Unknown response: %d\n", result);
1090 DO_QUIT_VOID ();
1092 rpc_get (msock, RPC_LIMITED_STRING, &password, RPC_END);
1093 logged_in = do_auth (username, password);
1094 endpwent ();
1095 login_reply (logged_in);
1098 /* }}} */
1100 /* {{{ Server and dispatching functions */
1102 /* This structure must be kept in synch with mcfs.h enums */
1104 static const struct _command {
1105 const char *command;
1106 void (*callback) (void);
1107 } commands[] = {
1109 "open", do_open}, {
1110 "close", do_close}, {
1111 "read", do_read}, {
1112 "write", do_write}, {
1113 "opendir", do_opendir}, {
1114 "readdir", do_readdir}, {
1115 "closedir", do_closedir}, {
1116 "stat ", do_stat}, {
1117 "lstat ", do_lstat}, {
1118 "fstat", do_fstat}, {
1119 "chmod", do_chmod}, {
1120 "chown", do_chown}, {
1121 "readlink ", do_readlink}, {
1122 "unlink", do_unlink}, {
1123 "rename", do_rename}, {
1124 "chdir ", do_chdir}, {
1125 "lseek", do_lseek}, {
1126 "rmdir", do_rmdir}, {
1127 "symlink", do_symlink}, {
1128 "mknod", do_mknod}, {
1129 "mkdir", do_mkdir}, {
1130 "link", do_link}, {
1131 "gethome", do_gethome}, {
1132 "getupdir", do_getupdir}, {
1133 "login", do_login}, {
1134 "quit", do_quit}, {
1135 "utime", do_utime}};
1137 static int ncommands = sizeof (commands) / sizeof (struct _command);
1139 static void
1140 exec_command (int command)
1142 if (command < 0 || command >= ncommands
1143 || commands[command].command == 0) {
1144 fprintf (stderr, "Got unknown command: %d\n", command);
1145 DO_QUIT_VOID ();
1147 if (verbose)
1148 printf ("Command: %s\n", commands[command].command);
1149 (*commands[command].callback) ();
1152 static void
1153 check_version (void)
1155 int version;
1157 rpc_get (msock, RPC_INT, &version, RPC_END);
1158 if (version >= 1 && version <= RPC_PROGVER)
1159 rpc_send (msock, RPC_INT, MC_VERSION_OK, RPC_END);
1160 else
1161 rpc_send (msock, RPC_INT, MC_VERSION_MISMATCH, RPC_END);
1163 clnt_version = version;
1166 /* This routine is called by rpc_get/rpc_send when the connection is closed */
1167 void
1168 tcp_invalidate_socket (int sock)
1170 if (verbose)
1171 printf ("Connection closed\n");
1172 DO_QUIT_VOID ();
1175 static void
1176 server (int sock)
1178 int command;
1180 msock = sock;
1181 quit_server = 0;
1183 check_version ();
1184 do {
1185 if (rpc_get (sock, RPC_INT, &command, RPC_END)
1186 && (logged_in || command == MC_LOGIN))
1187 exec_command (command);
1188 } while (!quit_server);
1191 /* }}} */
1193 /* {{{ Net support code */
1195 static const char *
1196 get_client (int portnum)
1198 int sock, newsocket;
1199 unsigned int clilen;
1200 struct sockaddr_in client_address, server_address;
1201 int yes = 1;
1203 if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
1204 return "Cannot create socket";
1206 /* Use this to debug: */
1207 if (setsockopt
1208 (sock, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof (yes)) < 0)
1209 return "setsockopt failed";
1211 memset ((char *) &server_address, 0, sizeof (server_address));
1212 server_address.sin_family = AF_INET;
1213 server_address.sin_addr.s_addr = htonl (INADDR_ANY);
1214 server_address.sin_port = htons (portnum);
1216 if (bind
1217 (sock, (struct sockaddr *) &server_address,
1218 sizeof (server_address)) < 0)
1219 return "Cannot bind";
1221 listen (sock, 5);
1223 for (;;) {
1224 int child;
1226 clilen = sizeof (client_address);
1227 newsocket =
1228 accept (sock, (struct sockaddr *) &client_address, &clilen);
1230 if (isDaemon && (child = fork ())) {
1231 int status;
1233 close (newsocket);
1234 waitpid (child, &status, 0);
1235 continue;
1238 if (isDaemon && fork ())
1239 exit (0);
1241 server (newsocket);
1242 close (newsocket);
1243 return 0;
1247 #ifdef HAVE_PMAP_SET
1248 static void
1249 signal_int_handler (int sig)
1251 (void) sig;
1253 pmap_unset (RPC_PROGNUM, RPC_PROGVER);
1255 #endif
1257 #ifndef IPPORT_RESERVED
1258 #define IPPORT_RESERVED 1024
1259 #endif
1261 static int
1262 get_port_number (void)
1264 int port = 0;
1266 #ifdef HAVE_RRESVPORT
1267 int start_port = IPPORT_RESERVED;
1269 port = rresvport (&start_port);
1270 if (port == -1) {
1271 if (geteuid () == 0) {
1272 fprintf (stderr,
1273 "Cannot bind the server on a reserved port\n");
1274 DO_QUIT_NONVOID (-1);
1276 port = 0;
1278 #endif
1279 if (port)
1280 return port;
1282 port = mcserver_port;
1284 return port;
1287 static void
1288 register_port (int portnum, int abort_if_fail)
1290 #ifdef HAVE_PMAP_SET
1291 /* Register our service with the portmapper */
1292 /* protocol: pmap_set (prognum, versnum, protocol, portp) */
1294 if (pmap_set (RPC_PROGNUM, RPC_PROGVER, IPPROTO_TCP, portnum))
1295 signal (SIGINT, signal_int_handler);
1296 else {
1297 fprintf (stderr, "Cannot register service with portmapper\n");
1298 if (abort_if_fail)
1299 exit (1);
1301 #else
1302 if (abort_if_fail) {
1303 fprintf (stderr,
1304 "This system lacks port registration, try using the -p\n"
1305 "flag to force installation at a given port");
1307 #endif
1310 /* }}} */
1313 main (int argc, char *argv[])
1315 const char *result;
1316 int c;
1318 while ((c = getopt (argc, argv, "fdiqp:v")) != -1) {
1319 switch (c) {
1320 case 'd':
1321 isDaemon = 1;
1322 verbose = 0;
1323 break;
1325 case 'v':
1326 verbose = 1;
1327 break;
1329 case 'f':
1330 ftp = 1;
1331 break;
1333 case 'q':
1334 verbose = 0;
1335 break;
1337 case 'p':
1338 portnum = atoi (optarg);
1339 break;
1341 case 'i':
1342 inetd_started = 1;
1343 break;
1345 case 'r':
1346 r_auth = 1;
1347 break;
1349 default:
1350 fprintf (stderr,
1351 "Usage is: mcserv [options] [-p portnum]\n\n"
1352 "options are:\n" "-d become a daemon (sets -q)\n"
1353 "-q quiet mode\n"
1354 /* "-r use rhost based authentication\n" */
1355 #ifndef HAVE_PAM
1356 "-f force ftp authentication\n"
1357 #endif
1358 "-v verbose mode\n"
1359 "-p to specify a port number to listen\n");
1360 exit (0);
1365 if (isDaemon && fork ())
1366 exit (0);
1368 if (portnum == 0)
1369 portnum = get_port_number ();
1371 if (portnum != -1) {
1372 register_port (portnum, 0);
1373 if (verbose)
1374 printf ("Using port %d\n", portnum);
1375 if ((result = get_client (portnum)))
1376 perror (result);
1377 #ifdef HAVE_PMAP_SET
1378 if (!isDaemon)
1379 pmap_unset (RPC_PROGNUM, RPC_PROGVER);
1380 #endif
1382 exit (return_code);
1385 /* FIXME: This function should not be used in mcserv */
1386 void
1387 vfs_die (const char *m)
1389 fputs (m, stderr);
1390 exit (1);