Last fuzzy trim
[midnight-commander.git] / vfs / mcserv.c
blob69b2df99b64e5592277e364f293950f61ff0fb90
1 /* Server for the Midnight Commander Virtual File System.
3 Copyright (C) 1995, 1996, 1997 The Free Software Foundation
5 Written by:
6 Miguel de Icaza, 1995, 1997,
7 Andrej Borsenkow 1996.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 TODO:
24 opendir instead of keeping its table of file handles could return
25 the pointer and expect the client to send a proper value back each
26 time :-)
28 We should use syslog to register login/logout.
32 /* {{{ Includes and global variables */
34 #include <config.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #ifdef HAVE_UNISTD_H
38 # include <unistd.h>
39 #endif
40 #include <fcntl.h>
41 #include <string.h>
42 #ifdef HAVE_LIMITS_H
43 # include <limits.h>
44 #endif
45 #ifndef NGROUPS_MAX
46 # include <sys/param.h>
47 # ifdef NGROUPS
48 # define NGROUPS_MAX NGROUPS
49 # endif
50 #endif
51 #ifdef HAVE_GRP_H
52 # include <grp.h>
53 #endif
54 #ifdef HAVE_SHADOW_H
55 # include <shadow.h>
56 #else
57 # ifdef HAVE_SHADOW_SHADOW_H
58 # include <shadow/shadow.h>
59 # endif
60 #endif
61 #ifdef HAVE_CRYPT_H
62 # include <crypt.h>
63 #endif
64 #include <sys/types.h>
65 #include <sys/stat.h>
66 #include <sys/wait.h>
67 #include <errno.h>
68 #include <signal.h>
70 /* Network include files */
71 #include <sys/socket.h>
72 #include <netinet/in.h>
73 #include <netdb.h>
74 #include <arpa/inet.h>
75 #ifdef HAVE_PMAP_SET
76 # include <rpc/rpc.h>
77 # include <rpc/pmap_prot.h>
78 # ifdef HAVE_RPC_PMAP_CLNT_H
79 # include <rpc/pmap_clnt.h>
80 # endif
81 #endif
83 /* Authentication include files */
84 #include <pwd.h>
85 #ifdef HAVE_PAM
86 # include <security/pam_misc.h>
87 # ifndef PAM_ESTABLISH_CRED
88 # define PAM_ESTABLISH_CRED PAM_CRED_ESTABLISH
89 # endif
90 #endif
92 #include "utilvfs.h"
94 #include "vfs.h"
95 #include "mcfs.h"
96 #include "tcputil.h"
98 /* The socket from which we accept commands */
99 int msock;
101 /* Requested version number from client */
102 static int clnt_version;
104 /* If non zero, we accept further commands */
105 int logged_in = 0;
107 /* Home directory */
108 char *home_dir = NULL;
110 char *up_dir = NULL;
112 /* Were we started from inetd? */
113 int inetd_started = 0;
115 /* Are we running as a daemon? */
116 int isDaemon = 0;
118 /* guess */
119 int verbose = 0;
121 /* ftp auth */
122 int ftp = 0;
124 /* port number in which we listen to connections,
125 * if zero, we try to contact the portmapper to get a port, and
126 * if it's not possible, then we use a hardcoded value
128 int portnum = 0;
130 /* if the server will use rcmd based authentication (hosts.equiv .rhosts) */
131 int r_auth = 0;
133 #define OPENDIR_HANDLES 8
135 #define DO_QUIT_VOID() \
136 do { \
137 quit_server = 1; \
138 return_code = 1; \
139 return; \
140 } while (0)
142 /* Only used by get_port_number */
143 #define DO_QUIT_NONVOID(a) \
144 do { \
145 quit_server = 1; \
146 return_code = 1; \
147 return (a); \
148 } while (0)
150 char buffer [4096];
151 int debug = 1;
152 static int quit_server;
153 static int return_code;
155 /* }}} */
157 /* {{{ Misc routines */
159 static void send_status (int status, int errno_number)
161 rpc_send (msock, RPC_INT, status, RPC_INT, errno_number, RPC_END);
162 errno = 0;
165 /* }}} */
167 /* {{{ File with handle operations */
169 static void do_open (void)
171 int handle, flags, mode;
172 char *arg;
174 rpc_get (msock, RPC_STRING, &arg, RPC_INT, &flags, RPC_INT, &mode,RPC_END);
176 handle = open (arg, flags, mode);
177 send_status (handle, errno);
178 g_free (arg);
181 static void do_read (void)
183 int handle, count, n;
184 void *data;
186 rpc_get (msock, RPC_INT, &handle, RPC_INT, &count, RPC_END);
187 data = g_malloc (count);
188 if (!data){
189 send_status (-1, ENOMEM);
190 return;
192 if (verbose) printf ("count=%d\n", count);
193 n = read (handle, data, count);
194 if (verbose) printf ("result=%d\n", n);
195 if (n < 0){
196 send_status (-1, errno);
197 return;
199 send_status (n, 0);
200 rpc_send (msock, RPC_BLOCK, n, data, RPC_END);
202 g_free (data);
205 static void do_write (void)
207 int handle, count, status, written = 0;
208 char buf[8192];
210 rpc_get (msock, RPC_INT, &handle, RPC_INT, &count, RPC_END);
211 status = 0;
212 while (count) {
213 int nbytes = count > 8192 ? 8192 : count;
215 rpc_get (msock, RPC_BLOCK, nbytes, buf, RPC_END);
216 status = write (handle, buf, nbytes);
217 if (status < 0) {
218 send_status (status, errno);
219 return;
221 /* FIXED: amount written must be returned to caller */
222 written += status;
223 if (status < nbytes) {
224 send_status (written, errno);
225 return;
227 count -= nbytes;
229 send_status (written, errno);
232 static void do_lseek (void)
234 int handle, offset, whence, status;
236 rpc_get (msock,
237 RPC_INT, &handle,
238 RPC_INT, &offset,
239 RPC_INT, &whence, RPC_END);
240 status = lseek (handle, offset, whence);
241 send_status (status, errno);
244 static void do_close (void)
246 int handle, status;
248 rpc_get (msock, RPC_INT, &handle, RPC_END);
249 status = close (handle);
250 send_status (status, errno);
253 /* }}} */
255 /* {{{ Stat family routines */
257 static void send_time (int sock, time_t time)
259 if (clnt_version == 1) {
260 char *ct;
261 int month;
263 ct = ctime (&time);
264 ct [3] = ct [10] = ct [13] = ct [16] = ct [19] = 0;
266 /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
267 if (ct [4] == 'J'){
268 if (ct [5] == 'a'){
269 month = 0;
270 } else
271 month = (ct [6] == 'n') ? 5 : 6;
272 } else if (ct [4] == 'F'){
273 month = 1;
274 } else if (ct [4] == 'M'){
275 month = (ct [6] == 'r') ? 2 : 5;
276 } else if (ct [4] == 'A'){
277 month = (ct [5] == 'p') ? 3 : 7;
278 } else if (ct [4] == 'S'){
279 month = 8;
280 } else if (ct [4] == 'O'){
281 month = 9;
282 } else if (ct [4] == 'N'){
283 month = 10;
284 } else
285 month = 11;
286 rpc_send (msock,
287 RPC_INT, atoi (&ct [17]), /* sec */
288 RPC_INT, atoi (&ct [14]), /* min */
289 RPC_INT, atoi (&ct [11]), /* hour */
290 RPC_INT, atoi (&ct [8]), /* mday */
291 RPC_INT, atoi (&ct [20]), /* year */
292 RPC_INT, month, /* month */
293 RPC_END);
294 } else {
295 long ltime = (long) time;
296 char buf[BUF_SMALL];
298 g_snprintf (buf, sizeof(buf), "%lx", ltime);
299 rpc_send (msock,
300 RPC_STRING, buf,
301 RPC_END);
305 static void send_stat_info (struct stat *st)
307 long mylong;
308 int blocks =
309 #ifdef HAVE_ST_BLOCKS
310 st->st_blocks;
311 #else
312 st->st_size / 1024;
313 #endif
315 #ifdef HAVE_ST_RDEV
316 mylong = st->st_rdev;
317 #else
318 mylong = 0;
319 #endif
320 rpc_send (msock, RPC_INT, (long) mylong,
321 RPC_INT, (long) st->st_ino,
322 RPC_INT, (long) st->st_mode,
323 RPC_INT, (long) st->st_nlink,
324 RPC_INT, (long) st->st_uid,
325 RPC_INT, (long) st->st_gid,
326 RPC_INT, (long) st->st_size,
327 RPC_INT, (long) blocks, RPC_END);
328 send_time (msock, st->st_atime);
329 send_time (msock, st->st_mtime);
330 send_time (msock, st->st_ctime);
333 static void do_lstat (void)
335 struct stat st;
336 char *file;
337 int n;
339 rpc_get (msock, RPC_STRING, &file, RPC_END);
340 n = lstat (file, &st);
341 send_status (n, errno);
342 if (n >= 0)
343 send_stat_info (&st);
344 g_free (file);
347 static void do_fstat (void)
349 int handle;
350 int n;
351 struct stat st;
353 rpc_get (msock, RPC_INT, &handle, RPC_END);
354 n = fstat (handle, &st);
355 send_status (n, errno);
356 if (n < 0)
357 return;
359 send_stat_info (&st);
362 static void do_stat (void)
364 struct stat st;
365 int n;
366 char *file;
368 rpc_get (msock, RPC_STRING, &file, RPC_END);
370 n = stat (file, &st);
371 send_status (n, errno);
372 if (n >= 0)
373 send_stat_info (&st);
374 g_free (file);
377 /* }}} */
379 /* {{{ Directory lookup operations */
381 static struct {
382 int used;
383 DIR *dirs [OPENDIR_HANDLES];
384 char *names [OPENDIR_HANDLES];
385 } mcfs_DIR;
387 static void close_handle (int handle)
389 if (mcfs_DIR.used > 0) mcfs_DIR.used--;
390 if (mcfs_DIR.dirs [handle])
391 closedir (mcfs_DIR.dirs [handle]);
392 if (mcfs_DIR.names [handle])
393 g_free (mcfs_DIR.names [handle]);
394 mcfs_DIR.dirs [handle] = 0;
395 mcfs_DIR.names [handle] = 0;
398 static void do_opendir (void)
400 int handle, i;
401 char *arg;
402 DIR *p;
404 rpc_get (msock, RPC_STRING, &arg, RPC_END);
406 if (mcfs_DIR.used == OPENDIR_HANDLES){
407 send_status (-1, ENFILE); /* Error */
408 g_free (arg);
409 return;
412 handle = -1;
413 for (i = 0; i < OPENDIR_HANDLES; i++){
414 if (mcfs_DIR.dirs [i] == 0){
415 handle = i;
416 break;
420 if (handle == -1){
421 send_status (-1, EMFILE);
422 g_free (arg);
423 if (!inetd_started)
424 fprintf (stderr, "OOPS! you have found a bug in mc - do_opendir()!\n");
425 return;
428 if (verbose) printf ("handle=%d\n", handle);
429 p = opendir (arg);
430 if (p){
431 mcfs_DIR.dirs [handle] = p;
432 mcfs_DIR.names [handle] = arg;
433 mcfs_DIR.used ++;
435 /* Because 0 is an error value */
436 rpc_send (msock, RPC_INT, handle+1, RPC_INT, 0, RPC_END);
438 } else {
439 send_status (-1, errno);
440 g_free (arg);
444 /* Sends the complete directory listing, as well as the stat information */
445 static void do_readdir (void)
447 struct dirent *dirent;
448 struct stat st;
449 int handle, n;
450 char *fname = 0;
452 rpc_get (msock, RPC_INT, &handle, RPC_END);
454 if (!handle){
455 rpc_send (msock, RPC_INT, 0, RPC_END);
456 return;
459 /* We incremented it in opendir */
460 handle --;
462 while ((dirent = readdir (mcfs_DIR.dirs [handle]))){
463 int length = NLENGTH (dirent);
465 rpc_send (msock, RPC_INT, length, RPC_END);
466 rpc_send (msock, RPC_BLOCK, length, dirent->d_name, RPC_END);
467 fname = g_strconcat (mcfs_DIR.names [handle],
468 PATH_SEP_STR, dirent->d_name, NULL);
469 n = lstat (fname, &st);
470 send_status (n, errno);
471 g_free (fname);
472 if (n >= 0)
473 send_stat_info (&st);
475 rpc_send (msock, RPC_INT, 0, RPC_END);
478 static void do_closedir (void)
480 int handle;
482 rpc_get (msock, RPC_INT, &handle, RPC_END);
483 close_handle (handle-1);
486 /* }}} */
488 /* {{{ Operations with one and two file name argument */
490 static void do_chdir (void)
492 char *file;
493 int status;
495 rpc_get (msock, RPC_STRING, &file, RPC_END);
497 status = chdir (file);
498 send_status (status, errno);
499 g_free (file);
502 static void do_rmdir (void)
504 char *file;
505 int status;
507 rpc_get (msock, RPC_STRING, &file, RPC_END);
509 status = rmdir (file);
510 send_status (status, errno);
511 g_free (file);
514 static void do_mkdir (void)
516 char *file;
517 int mode, status;
519 rpc_get (msock, RPC_STRING, &file, RPC_INT, &mode, RPC_END);
521 status = mkdir (file, mode);
522 send_status (status, errno);
523 g_free (file);
526 static void do_mknod (void)
528 char *file;
529 int mode, dev, status;
531 rpc_get (msock, RPC_STRING, &file, RPC_INT, &mode, RPC_INT, &dev, RPC_END);
533 status = mknod (file, mode, dev);
534 send_status (status, errno);
535 g_free (file);
538 static void do_readlink (void)
540 char buffer [2048];
541 char *file;
542 int n;
544 rpc_get (msock, RPC_STRING, &file, RPC_END);
545 n = readlink (file, buffer, 2048);
546 send_status (n, errno);
547 if (n >= 0) {
548 buffer [n] = 0;
549 rpc_send (msock, RPC_STRING, buffer, RPC_END);
551 g_free (file);
554 static void do_unlink (void)
556 char *file;
557 int status;
559 rpc_get (msock, RPC_STRING, &file, RPC_END);
560 status = unlink (file);
561 send_status (status, errno);
562 g_free (file);
565 static void do_rename (void)
567 char *f1, *f2;
568 int status;
570 rpc_get (msock, RPC_STRING, &f1, RPC_STRING, &f2, RPC_END);
571 status = rename (f1, f2);
572 send_status (status, errno);
573 g_free (f1); g_free (f2);
576 static void do_symlink (void)
578 char *f1, *f2;
579 int status;
581 rpc_get (msock, RPC_STRING, &f1, RPC_STRING, &f2, RPC_END);
582 status = symlink (f1, f2);
583 send_status (status, errno);
584 g_free (f1); g_free (f2);
587 static void do_link (void)
589 char *f1, *f2;
590 int status;
592 rpc_get (msock, RPC_STRING, &f1, RPC_STRING, &f2, RPC_END);
593 status = link (f1, f2);
594 send_status (status, errno);
595 g_free (f1); g_free (f2);
599 /* }}} */
601 /* {{{ Misc commands */
603 static void do_gethome (void)
605 rpc_send (msock, RPC_STRING, (home_dir) ? home_dir : "/", RPC_END);
608 static void do_getupdir (void)
610 rpc_send (msock, RPC_STRING, (up_dir) ? up_dir : "/", RPC_END);
613 static void do_chmod (void)
615 char *file;
616 int mode, status;
618 rpc_get (msock, RPC_STRING, &file, RPC_INT, &mode, RPC_END);
619 status = chmod (file, mode);
620 send_status (status, errno);
621 g_free (file);
624 static void do_chown (void)
626 char *file;
627 int owner, group, status;
629 rpc_get (msock, RPC_STRING, &file,RPC_INT, &owner, RPC_INT,&group,RPC_END);
630 status = chown (file, owner, group);
631 send_status (status, errno);
632 g_free (file);
635 static void do_utime (void)
637 char *file;
638 int status;
639 long atime;
640 long mtime;
641 char *as;
642 char *ms;
643 struct utimbuf times;
645 rpc_get (msock, RPC_STRING, &file,
646 RPC_STRING, &as,
647 RPC_STRING, &ms,
648 RPC_END);
649 sscanf (as, "%lx", &atime);
650 sscanf (ms, "%lx", &mtime);
651 if (verbose) printf ("Got a = %s, m = %s, comp a = %ld, m = %ld\n",
652 as, ms, atime, mtime);
653 g_free (as);
654 g_free (ms);
655 times.actime = (time_t) atime;
656 times.modtime = (time_t) mtime;
657 status = utime (file, &times);
658 send_status (status, errno);
659 g_free (file);
662 static void do_quit (void)
664 quit_server = 1;
667 #ifdef HAVE_PAM
669 struct user_pass {
670 char *username;
671 char *password;
674 static int
675 mc_pam_conversation (int messages, const struct pam_message **msg,
676 struct pam_response **resp, void *appdata_ptr)
678 struct pam_response *r;
679 struct user_pass *up = appdata_ptr;
680 int status;
682 r = g_new (struct pam_response, messages);
683 if (!r)
684 return PAM_CONV_ERR;
685 *resp = r;
687 for (status = PAM_SUCCESS; messages--; msg++, r++){
688 switch ((*msg)->msg_style){
690 case PAM_PROMPT_ECHO_ON:
691 r->resp = g_strdup (up->username);
692 r->resp_retcode = PAM_SUCCESS;
693 break;
695 case PAM_PROMPT_ECHO_OFF:
696 r->resp = g_strdup (up->password);
697 r->resp_retcode = PAM_SUCCESS;
698 break;
700 case PAM_ERROR_MSG:
701 r->resp = NULL;
702 r->resp_retcode = PAM_SUCCESS;
703 break;
705 case PAM_TEXT_INFO:
706 r->resp = NULL;
707 r->resp_retcode = PAM_SUCCESS;
708 break;
711 return status;
714 static struct pam_conv conv = { &mc_pam_conversation, NULL };
717 /* Return 0 if authentication failed, 1 otherwise */
718 static int
719 mc_pam_auth (char *username, char *password)
721 pam_handle_t *pamh;
722 struct user_pass up;
723 int status;
725 up.username = username;
726 up.password = password;
727 conv.appdata_ptr = &up;
729 if ((status = pam_start("mcserv", username, &conv, &pamh)) != PAM_SUCCESS)
730 goto failed_pam;
731 if ((status = pam_authenticate (pamh, 0)) != PAM_SUCCESS)
732 goto failed_pam;
733 if ((status = pam_acct_mgmt (pamh, 0)) != PAM_SUCCESS)
734 goto failed_pam;
735 if ((status = pam_setcred (pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
736 goto failed_pam;
737 pam_end (pamh, status);
738 return 0;
740 failed_pam:
741 pam_end (pamh, status);
742 return 1;
745 #else /* Code for non-PAM authentication */
747 /* Keep reading until we find a \n */
748 static int next_line (int socket)
750 char c;
752 while (1){
753 if (read (socket, &c, 1) <= 0)
754 return 0;
755 if (c == '\n')
756 return 1;
760 static int ftp_answer (int sock, char *text)
762 char answer [4];
764 next_line (sock);
765 socket_read_block (sock, answer, 3);
766 answer [3] = 0;
767 if (strcmp (answer, text) == 0)
768 return 1;
769 return 0;
772 static int do_ftp_auth (char *username, char *password)
774 struct sockaddr_in local_address;
775 unsigned long inaddr;
776 int my_socket;
777 char answer [4];
779 memset ((char *) &local_address, 0, sizeof (local_address));
781 local_address.sin_family = AF_INET;
782 /* FIXME: extract the ftp port with the proper function */
783 local_address.sin_port = htons (21);
785 /* Convert localhost to usable format */
786 if ((inaddr = inet_addr ("127.0.0.1")) != -1)
787 memcpy ((char *) &local_address.sin_addr, (char *) &inaddr,
788 sizeof (inaddr));
790 if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0){
791 if (!isDaemon) fprintf (stderr, "do_auth: can't create socket\n");
792 return 0;
794 if (connect (my_socket, (struct sockaddr *) &local_address,
795 sizeof (local_address)) < 0){
796 fprintf (stderr,
797 "do_auth: can't connect to ftp daemon for authentication\n");
798 close (my_socket);
799 return 0;
801 send_string (my_socket, "user ");
802 send_string (my_socket, username);
803 send_string (my_socket, "\r\n");
804 if (!ftp_answer (my_socket, "331")){
805 send_string (my_socket, "quit\r\n");
806 close (my_socket);
807 return 0;
809 next_line (my_socket); /* Eat all the line */
810 send_string (my_socket, "pass ");
811 send_string (my_socket, password);
812 send_string (my_socket, "\r\n");
813 socket_read_block (my_socket, answer, 3);
814 answer [3] = 0;
815 send_string (my_socket, "\r\n");
816 send_string (my_socket, "quit\r\n");
817 close (my_socket);
818 if (strcmp (answer, "230") == 0)
819 return 1;
820 return 0;
823 static int do_classic_auth (char *username, char *password)
825 struct passwd *this;
826 int ret;
827 #ifdef LINUX_SHADOW
828 struct spwd *spw;
829 extern char *pw_encrypt (char *, char *);
830 #else
831 #ifdef NEED_CRYPT_PROTOTYPE
832 extern char *crypt (const char *, const char *);
833 #endif
834 #endif
836 if ((this = getpwnam (username)) == 0)
837 return 0;
839 #ifdef LINUX_SHADOW
840 if ((spw = getspnam (username)) == NULL)
841 this->pw_passwd = "*";
842 else
843 this->pw_passwd = spw->sp_pwdp;
844 if (strcmp (pw_encrypt (password, this->pw_passwd), this->pw_passwd) == 0)
845 ret = 1;
846 else
847 ret = 0;
848 #else
849 #ifdef HAVE_CRYPT
850 if (strcmp (crypt (password, this->pw_passwd), this->pw_passwd) == 0){
851 ret = 1;
852 } else
853 #endif
855 ret = 0;
857 #endif
858 endpwent ();
859 return ret;
861 #endif /* non-PAM authentication */
863 /* Try to authenticate the user based on:
864 - PAM if the system has it, else it checks:
865 - pwdauth if the system supports it.
866 - conventional auth (check salt on /etc/passwd, crypt, and compare
867 - try to contact the local ftp server and login (if -f flag used)
869 static int
870 do_auth (char *username, char *password)
872 int auth = 0;
873 struct passwd *this;
875 if (strcmp (username, "anonymous") == 0)
876 username = "ftp";
878 #ifdef HAVE_PAM
879 if (mc_pam_auth (username, password) == 0)
880 auth = 1;
881 #else /* if there is no pam */
882 #ifdef HAVE_PWDAUTH
883 if (pwdauth (username, password) == 0)
884 auth = 1;
885 else
886 #endif
887 if (do_classic_auth (username, password))
888 auth = 1;
889 else if (ftp)
890 auth = do_ftp_auth (username, password);
891 #endif /* not pam */
893 if (!auth)
894 return 0;
896 this = getpwnam (username);
897 if (this == 0)
898 return 0;
900 if (chdir (this->pw_dir) == -1)
901 return 0;
903 if (this->pw_dir [strlen (this->pw_dir) - 1] == '/')
904 home_dir = g_strdup (this->pw_dir);
905 else {
906 home_dir = g_malloc (strlen (this->pw_dir) + 2);
907 if (home_dir) {
908 strcpy (home_dir, this->pw_dir);
909 strcat (home_dir, "/");
910 } else
911 home_dir = "/";
915 if (setgid (this->pw_gid) == -1)
916 return 0;
918 #ifdef HAVE_INITGROUPS
919 #ifdef NGROUPS_MAX
920 if (NGROUPS_MAX > 1 && initgroups (this->pw_name, this->pw_gid))
921 return 0;
922 #endif
923 #endif
925 #if defined (HAVE_SETUID)
926 if (setuid (this->pw_uid))
927 return 0;
928 #elif defined (HAVE_SETREUID)
929 if (setreuid (this->pw_uid, this->pw_uid))
930 return 0;
931 #endif
933 /* If the setuid call failed, then deny access */
934 /* This should fix the problem on those machines with strange setups */
935 if (getuid () != this->pw_uid)
936 return 0;
938 if (strcmp (username, "ftp") == 0)
939 chroot (this->pw_dir);
941 endpwent ();
942 return auth;
945 #if 0
946 static int do_rauth (int socket)
948 struct sockaddr_in from;
949 struct hostent *hp;
951 if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0)
952 return 0;
953 from.sin_port = ntohs ((unsigned short) from.sin_port);
955 /* Strange, this should not happend */
956 if (from.sin_family != AF_INET)
957 return 0;
959 hp = gethostbyaddr((char *)&fromp.sin_addr, sizeof (struct in_addr),
960 fromp.sin_family);
963 #endif
965 static int do_rauth (int msock)
967 return 0;
970 static void login_reply (int logged_in)
972 rpc_send (msock, RPC_INT,
973 logged_in ? MC_LOGINOK : MC_INVALID_PASS,
974 RPC_END);
977 /* FIXME: Implement the anonymous login */
978 static void do_login (void)
980 char *username;
981 char *password;
982 int result;
984 rpc_get (msock, RPC_LIMITED_STRING, &up_dir, RPC_LIMITED_STRING, &username, RPC_END);
985 if (verbose) printf ("username: %s\n", username);
987 if (r_auth){
988 logged_in = do_rauth (msock);
989 if (logged_in){
990 login_reply (logged_in);
991 return;
994 rpc_send (msock, RPC_INT, MC_NEED_PASSWORD, RPC_END);
995 rpc_get (msock, RPC_INT, &result, RPC_END);
996 if (result == MC_QUIT)
997 DO_QUIT_VOID ();
998 if (result != MC_PASS){
999 if (verbose) printf ("do_login: Unknown response: %d\n", result);
1000 DO_QUIT_VOID ();
1002 rpc_get (msock, RPC_LIMITED_STRING, &password, RPC_END);
1003 logged_in = do_auth (username, password);
1004 endpwent ();
1005 login_reply (logged_in);
1008 /* }}} */
1010 /* {{{ Server and dispatching functions */
1012 /* This structure must be kept in synch with mcfs.h enums */
1014 static struct _command {
1015 char *command;
1016 void (*callback)(void);
1017 } commands [] = {
1018 { "open", do_open },
1019 { "close", do_close },
1020 { "read", do_read },
1021 { "write", do_write },
1022 { "opendir", do_opendir },
1023 { "readdir", do_readdir },
1024 { "closedir", do_closedir },
1025 { "stat ", do_stat },
1026 { "lstat ", do_lstat },
1027 { "fstat", do_fstat },
1028 { "chmod", do_chmod },
1029 { "chown", do_chown },
1030 { "readlink ", do_readlink },
1031 { "unlink", do_unlink },
1032 { "rename", do_rename },
1033 { "chdir ", do_chdir },
1034 { "lseek", do_lseek },
1035 { "rmdir", do_rmdir },
1036 { "symlink", do_symlink },
1037 { "mknod", do_mknod },
1038 { "mkdir", do_mkdir },
1039 { "link", do_link },
1040 { "gethome", do_gethome },
1041 { "getupdir", do_getupdir },
1042 { "login", do_login },
1043 { "quit", do_quit },
1044 { "utime", do_utime },
1047 static int ncommands = sizeof(commands)/sizeof(struct _command);
1049 static void exec_command (int command)
1051 if (command < 0 ||
1052 command >= ncommands ||
1053 commands [command].command == 0){
1054 fprintf (stderr, "Got unknown command: %d\n", command);
1055 DO_QUIT_VOID ();
1057 if (verbose) printf ("Command: %s\n", commands [command].command);
1058 (*commands [command].callback)();
1061 static void check_version (void)
1063 int version;
1065 rpc_get (msock, RPC_INT, &version, RPC_END);
1066 if (version >= 1 &&
1067 version <= RPC_PROGVER)
1068 rpc_send (msock, RPC_INT, MC_VERSION_OK, RPC_END);
1069 else
1070 rpc_send (msock, RPC_INT, MC_VERSION_MISMATCH, RPC_END);
1072 clnt_version = version;
1075 /* This routine is called by rpc_get/rpc_send when the connection is closed */
1076 void tcp_invalidate_socket (int sock)
1078 if (verbose) printf ("Connection closed\n");
1079 DO_QUIT_VOID();
1082 static void server (int sock)
1084 int command;
1086 msock = sock;
1087 quit_server = 0;
1089 check_version ();
1090 do {
1091 if (rpc_get (sock, RPC_INT, &command, RPC_END) &&
1092 (logged_in || command == MC_LOGIN))
1093 exec_command (command);
1094 } while (!quit_server);
1097 /* }}} */
1099 /* {{{ Net support code */
1101 static char *get_client (int portnum)
1103 int sock, clilen, newsocket;
1104 struct sockaddr_in client_address, server_address;
1105 struct hostent *hp;
1106 char hostname [255];
1107 int yes = 1;
1108 #ifdef __EMX__
1109 char *me;
1110 #endif
1112 if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
1113 return "Cannot create socket";
1115 /* Use this to debug: */
1116 if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof (yes)) < 0)
1117 return "setsockopt failed";
1119 gethostname (hostname, 255);
1120 if (verbose) printf ("hostname=%s\n", hostname);
1121 hp = gethostbyname (hostname);
1122 #ifdef __EMX__
1123 if (hp == 0 && (me = getenv("HOSTNAME")) && (0 == strcmp(hostname, me)))
1124 hp = gethostbyname ("localhost");
1125 #endif
1126 if (hp == 0)
1127 return "hp = 0!";
1129 memset ((char *) &server_address, 0, sizeof (server_address));
1130 server_address.sin_family = hp->h_addrtype;
1131 server_address.sin_addr.s_addr = htonl (INADDR_ANY);
1132 server_address.sin_port = htons (portnum);
1134 if (bind (sock, (struct sockaddr *) &server_address,
1135 sizeof (server_address)) < 0)
1136 return "Cannot bind";
1138 listen (sock, 5);
1140 for (;;){
1141 int child;
1143 clilen = sizeof (client_address);
1144 newsocket = accept (sock, (struct sockaddr *) &client_address,
1145 &clilen);
1147 if (isDaemon && (child = fork())) {
1148 int status;
1150 close (newsocket);
1151 waitpid (child, &status, 0);
1152 continue;
1155 if (isDaemon && fork()) exit (0);
1157 server (newsocket);
1158 close (newsocket);
1159 return 0;
1163 #ifdef HAVE_PMAP_SET
1164 static void signal_int_handler (int sig)
1166 pmap_unset (RPC_PROGNUM, RPC_PROGVER);
1168 #endif
1170 #ifndef IPPORT_RESERVED
1171 #define IPPORT_RESERVED 1024;
1172 #endif
1174 static int get_port_number (void)
1176 int port = 0;
1178 #ifdef HAVE_RRESVPORT
1179 int start_port = IPPORT_RESERVED;
1181 port = rresvport (&start_port);
1182 if (port == -1){
1183 if (geteuid () == 0){
1184 fprintf (stderr, "Could not bind the server on a reserved port\n");
1185 DO_QUIT_NONVOID (-1);
1187 port = 0;
1189 #endif
1190 if (port)
1191 return port;
1193 port = mcserver_port;
1195 return port;
1198 static void register_port (int portnum, int abort_if_fail)
1200 #ifdef HAVE_PMAP_SET
1201 /* Register our service with the portmapper */
1202 /* protocol: pmap_set (prognum, versnum, protocol, portp) */
1204 if (pmap_set (RPC_PROGNUM, RPC_PROGVER, IPPROTO_TCP, portnum))
1205 signal (SIGINT, signal_int_handler);
1206 else {
1207 fprintf (stderr, "Could not register service with portmapper\n");
1208 if (abort_if_fail)
1209 exit (1);
1211 #else
1212 if (abort_if_fail){
1213 fprintf (stderr,
1214 "This system lacks port registration, try using the -p\n"
1215 "flag to force installation at a given port");
1217 #endif
1220 /* }}} */
1222 int main (int argc, char *argv [])
1224 char *result;
1225 extern char *optarg;
1226 int c;
1228 while ((c = getopt (argc, argv, "fdiqp:v")) != -1){
1229 switch (c){
1230 case 'd':
1231 isDaemon = 1;
1232 verbose = 0;
1233 break;
1235 case 'v':
1236 verbose = 1;
1237 break;
1239 case 'f':
1240 ftp = 1;
1241 break;
1243 case 'q':
1244 verbose = 0;
1245 break;
1247 case 'p':
1248 portnum = atoi (optarg);
1249 break;
1251 case 'i':
1252 inetd_started = 1;
1253 break;
1255 case 'r':
1256 r_auth = 1;
1257 break;
1259 default:
1260 fprintf (stderr, "Usage is: mcserv [options] [-p portnum]\n\n"
1261 "options are:\n"
1262 "-d become a daemon (sets -q)\n"
1263 "-q quiet mode\n"
1264 /* "-r use rhost based authentication\n" */
1265 #ifndef HAVE_PAM
1266 "-f force ftp authentication\n"
1267 #endif
1268 "-v verbose mode\n"
1269 "-p to specify a port number to listen\n");
1270 exit (0);
1275 if (isDaemon && fork()) exit (0);
1277 if (portnum == 0)
1278 portnum = get_port_number ();
1280 if (portnum != -1) {
1281 register_port (portnum, 0);
1282 if (verbose)
1283 printf ("Using port %d\n", portnum);
1284 if ((result = get_client (portnum)))
1285 perror (result);
1286 #ifdef HAVE_PMAP_SET
1287 if (!isDaemon)
1288 pmap_unset (RPC_PROGNUM, RPC_PROGVER);
1289 #endif
1291 exit (return_code);
1294 /* FIXME: This function should not be used in mcserv */
1295 void vfs_die( char *m )
1297 fputs (m, stderr);
1298 exit (1);