mc.sh & mc.csh creation fixed...
[midnight-commander.git] / vfs / mcserv.c
blobaffc04d646caa261d3b8b05c817c9782abe02eaf
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 <string.h>
41 #ifdef HAVE_LIMITS_H
42 # include <limits.h>
43 #endif
44 #ifndef NGROUPS_MAX
45 # include <sys/param.h>
46 # ifdef NGROUPS
47 # define NGROUPS_MAX NGROUPS
48 # endif
49 #endif
50 #ifdef HAVE_GRP_H
51 # include <grp.h>
52 #endif
53 #include <sys/types.h>
54 #include <sys/stat.h>
55 #include <sys/wait.h>
56 #include <errno.h>
57 #include <signal.h>
59 /* Network include files */
60 #include <sys/socket.h>
61 #include <netinet/in.h>
62 #include <netdb.h>
63 #ifdef HAVE_ARPA_INET_H
64 #include <arpa/inet.h>
65 #endif
66 #ifdef HAVE_PMAP_SET
67 # include <rpc/rpc.h>
68 # include <rpc/pmap_prot.h>
69 # ifdef HAVE_RPC_PMAP_CLNT_H
70 # include <rpc/pmap_clnt.h>
71 # endif
72 #endif
74 /* Authentication include files */
75 #include <pwd.h>
76 #ifdef HAVE_PAM
77 # include <security/pam_misc.h>
78 # ifndef PAM_ESTABLISH_CRED
79 # define PAM_ESTABLISH_CRED PAM_CRED_ESTABLISH
80 # endif
81 #else
82 #ifdef HAVE_CRYPT_H
83 # include <crypt.h>
84 #else
85 extern char *crypt (const char *, const char *);
86 #endif /* !HAVE_CRYPT_H */
87 #endif /* !HAVE_PAM */
89 #include "utilvfs.h"
91 #include "vfs.h"
92 #include "mcfs.h"
93 #include "mcfsutil.h"
94 #include "tcputil.h"
96 /* replacement for g_free() from glib */
97 #undef g_free
98 #define g_free(x) do {if (x) free (x);} while (0)
100 /* We don't care about SIGPIPE */
101 int got_sigpipe = 0;
103 /* The socket from which we accept commands */
104 int msock;
106 /* Requested version number from client */
107 static int clnt_version;
109 /* If non zero, we accept further commands */
110 int logged_in = 0;
112 /* Home directory */
113 char *home_dir = NULL;
115 char *up_dir = NULL;
117 /* Were we started from inetd? */
118 int inetd_started = 0;
120 /* Are we running as a daemon? */
121 int isDaemon = 0;
123 /* guess */
124 int verbose = 0;
126 /* ftp auth */
127 int ftp = 0;
129 /* port number in which we listen to connections,
130 * if zero, we try to contact the portmapper to get a port, and
131 * if it's not possible, then we use a hardcoded value
133 int portnum = 0;
135 /* if the server will use rcmd based authentication (hosts.equiv .rhosts) */
136 int r_auth = 0;
138 #define OPENDIR_HANDLES 8
140 #define DO_QUIT_VOID() \
141 do { \
142 quit_server = 1; \
143 return_code = 1; \
144 return; \
145 } while (0)
147 /* Only used by get_port_number */
148 #define DO_QUIT_NONVOID(a) \
149 do { \
150 quit_server = 1; \
151 return_code = 1; \
152 return (a); \
153 } while (0)
155 char buffer[4096];
156 int debug = 1;
157 static int quit_server;
158 static int return_code;
160 /* }}} */
162 /* {{{ Misc routines */
164 static void
165 send_status (int status, int errno_number)
167 rpc_send (msock, RPC_INT, status, RPC_INT, errno_number, RPC_END);
168 errno = 0;
171 /* }}} */
173 /* {{{ File with handle operations */
175 static void
176 do_open (void)
178 int handle, flags, mode;
179 char *arg;
181 rpc_get (msock, RPC_STRING, &arg, RPC_INT, &flags, RPC_INT, &mode,
182 RPC_END);
184 handle = open (arg, flags, mode);
185 send_status (handle, errno);
186 g_free (arg);
189 static void
190 do_read (void)
192 int handle, count, n;
193 void *data;
195 rpc_get (msock, RPC_INT, &handle, RPC_INT, &count, RPC_END);
196 data = malloc (count);
197 if (!data) {
198 send_status (-1, ENOMEM);
199 return;
201 if (verbose)
202 printf ("count=%d\n", count);
203 n = read (handle, data, count);
204 if (verbose)
205 printf ("result=%d\n", n);
206 if (n < 0) {
207 send_status (-1, errno);
208 return;
210 send_status (n, 0);
211 rpc_send (msock, RPC_BLOCK, n, data, RPC_END);
213 g_free (data);
216 static void
217 do_write (void)
219 int handle, count, status, written = 0;
220 char buf[8192];
222 rpc_get (msock, RPC_INT, &handle, RPC_INT, &count, RPC_END);
223 status = 0;
224 while (count) {
225 int nbytes = count > 8192 ? 8192 : count;
227 rpc_get (msock, RPC_BLOCK, nbytes, buf, RPC_END);
228 status = write (handle, buf, nbytes);
229 if (status < 0) {
230 send_status (status, errno);
231 return;
233 /* FIXED: amount written must be returned to caller */
234 written += status;
235 if (status < nbytes) {
236 send_status (written, errno);
237 return;
239 count -= nbytes;
241 send_status (written, errno);
244 static void
245 do_lseek (void)
247 int handle, offset, whence, status;
249 rpc_get (msock,
250 RPC_INT, &handle,
251 RPC_INT, &offset, RPC_INT, &whence, RPC_END);
252 status = lseek (handle, offset, whence);
253 send_status (status, errno);
256 static void
257 do_close (void)
259 int handle, status;
261 rpc_get (msock, RPC_INT, &handle, RPC_END);
262 status = close (handle);
263 send_status (status, errno);
266 /* }}} */
268 /* {{{ Stat family routines */
270 static void
271 send_time (int sock, time_t time)
273 if (clnt_version == 1) {
274 char *ct;
275 int month;
277 ct = ctime (&time);
278 ct[3] = ct[10] = ct[13] = ct[16] = ct[19] = 0;
280 /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
281 if (ct[4] == 'J') {
282 if (ct[5] == 'a') {
283 month = 0;
284 } else
285 month = (ct[6] == 'n') ? 5 : 6;
286 } else if (ct[4] == 'F') {
287 month = 1;
288 } else if (ct[4] == 'M') {
289 month = (ct[6] == 'r') ? 2 : 5;
290 } else if (ct[4] == 'A') {
291 month = (ct[5] == 'p') ? 3 : 7;
292 } else if (ct[4] == 'S') {
293 month = 8;
294 } else if (ct[4] == 'O') {
295 month = 9;
296 } else if (ct[4] == 'N') {
297 month = 10;
298 } else
299 month = 11;
300 rpc_send (msock, RPC_INT, atoi (&ct[17]), /* sec */
301 RPC_INT, atoi (&ct[14]), /* min */
302 RPC_INT, atoi (&ct[11]), /* hour */
303 RPC_INT, atoi (&ct[8]), /* mday */
304 RPC_INT, atoi (&ct[20]), /* year */
305 RPC_INT, month, /* month */
306 RPC_END);
307 } else {
308 long ltime = (long) time;
309 char buf[BUF_SMALL];
311 snprintf (buf, sizeof (buf), "%lx", ltime);
312 rpc_send (msock, RPC_STRING, buf, RPC_END);
316 static void
317 send_stat_info (struct stat *st)
319 long mylong;
320 int blocks =
321 #ifdef HAVE_ST_BLOCKS
322 st->st_blocks;
323 #else
324 st->st_size / 1024;
325 #endif
327 #ifdef HAVE_ST_RDEV
328 mylong = st->st_rdev;
329 #else
330 mylong = 0;
331 #endif
332 rpc_send (msock, RPC_INT, (long) mylong,
333 RPC_INT, (long) st->st_ino,
334 RPC_INT, (long) st->st_mode,
335 RPC_INT, (long) st->st_nlink,
336 RPC_INT, (long) st->st_uid,
337 RPC_INT, (long) st->st_gid,
338 RPC_INT, (long) st->st_size,
339 RPC_INT, (long) blocks, RPC_END);
340 send_time (msock, st->st_atime);
341 send_time (msock, st->st_mtime);
342 send_time (msock, st->st_ctime);
345 static void
346 do_lstat (void)
348 struct stat st;
349 char *file;
350 int n;
352 rpc_get (msock, RPC_STRING, &file, RPC_END);
353 n = lstat (file, &st);
354 send_status (n, errno);
355 if (n >= 0)
356 send_stat_info (&st);
357 g_free (file);
360 static void
361 do_fstat (void)
363 int handle;
364 int n;
365 struct stat st;
367 rpc_get (msock, RPC_INT, &handle, RPC_END);
368 n = fstat (handle, &st);
369 send_status (n, errno);
370 if (n < 0)
371 return;
373 send_stat_info (&st);
376 static void
377 do_stat (void)
379 struct stat st;
380 int n;
381 char *file;
383 rpc_get (msock, RPC_STRING, &file, RPC_END);
385 n = stat (file, &st);
386 send_status (n, errno);
387 if (n >= 0)
388 send_stat_info (&st);
389 g_free (file);
392 /* }}} */
394 /* {{{ Directory lookup operations */
396 static struct {
397 int used;
398 DIR *dirs[OPENDIR_HANDLES];
399 char *names[OPENDIR_HANDLES];
400 } mcfs_DIR;
402 static void
403 close_handle (int handle)
405 if (mcfs_DIR.used > 0)
406 mcfs_DIR.used--;
407 if (mcfs_DIR.dirs[handle])
408 closedir (mcfs_DIR.dirs[handle]);
409 if (mcfs_DIR.names[handle])
410 g_free (mcfs_DIR.names[handle]);
411 mcfs_DIR.dirs[handle] = 0;
412 mcfs_DIR.names[handle] = 0;
415 static void
416 do_opendir (void)
418 int handle, i;
419 char *arg;
420 DIR *p;
422 rpc_get (msock, RPC_STRING, &arg, RPC_END);
424 if (mcfs_DIR.used == OPENDIR_HANDLES) {
425 send_status (-1, ENFILE); /* Error */
426 g_free (arg);
427 return;
430 handle = -1;
431 for (i = 0; i < OPENDIR_HANDLES; i++) {
432 if (mcfs_DIR.dirs[i] == 0) {
433 handle = i;
434 break;
438 if (handle == -1) {
439 send_status (-1, EMFILE);
440 g_free (arg);
441 if (!inetd_started)
442 fprintf (stderr,
443 "OOPS! you have found a bug in mc - do_opendir()!\n");
444 return;
447 if (verbose)
448 printf ("handle=%d\n", handle);
449 p = opendir (arg);
450 if (p) {
451 mcfs_DIR.dirs[handle] = p;
452 mcfs_DIR.names[handle] = arg;
453 mcfs_DIR.used++;
455 /* Because 0 is an error value */
456 rpc_send (msock, RPC_INT, handle + 1, RPC_INT, 0, RPC_END);
458 } else {
459 send_status (-1, errno);
460 g_free (arg);
464 /* Sends the complete directory listing, as well as the stat information */
465 static void
466 do_readdir (void)
468 struct dirent *dirent;
469 struct stat st;
470 int handle, n;
472 rpc_get (msock, RPC_INT, &handle, RPC_END);
474 if (!handle) {
475 rpc_send (msock, RPC_INT, 0, RPC_END);
476 return;
479 /* We incremented it in opendir */
480 handle--;
482 while ((dirent = readdir (mcfs_DIR.dirs[handle]))) {
483 int fname_len;
484 char *fname;
485 int length = NLENGTH (dirent);
487 rpc_send (msock, RPC_INT, length, RPC_END);
488 rpc_send (msock, RPC_BLOCK, length, dirent->d_name, RPC_END);
489 fname_len =
490 strlen (mcfs_DIR.names[handle]) + strlen (dirent->d_name) + 2;
491 fname = malloc (fname_len);
492 snprintf (fname, fname_len, "%s/%s", mcfs_DIR.names[handle],
493 dirent->d_name);
494 n = lstat (fname, &st);
495 g_free (fname);
496 send_status (n, errno);
497 if (n >= 0)
498 send_stat_info (&st);
500 rpc_send (msock, RPC_INT, 0, RPC_END);
503 static void
504 do_closedir (void)
506 int handle;
508 rpc_get (msock, RPC_INT, &handle, RPC_END);
509 close_handle (handle - 1);
512 /* }}} */
514 /* {{{ Operations with one and two file name argument */
516 static void
517 do_chdir (void)
519 char *file;
520 int status;
522 rpc_get (msock, RPC_STRING, &file, RPC_END);
524 status = chdir (file);
525 send_status (status, errno);
526 g_free (file);
529 static void
530 do_rmdir (void)
532 char *file;
533 int status;
535 rpc_get (msock, RPC_STRING, &file, RPC_END);
537 status = rmdir (file);
538 send_status (status, errno);
539 g_free (file);
542 static void
543 do_mkdir (void)
545 char *file;
546 int mode, status;
548 rpc_get (msock, RPC_STRING, &file, RPC_INT, &mode, RPC_END);
550 status = mkdir (file, mode);
551 send_status (status, errno);
552 g_free (file);
555 static void
556 do_mknod (void)
558 char *file;
559 int mode, dev, status;
561 rpc_get (msock, RPC_STRING, &file, RPC_INT, &mode, RPC_INT, &dev,
562 RPC_END);
564 status = mknod (file, mode, dev);
565 send_status (status, errno);
566 g_free (file);
569 static void
570 do_readlink (void)
572 char buffer[2048];
573 char *file;
574 int n;
576 rpc_get (msock, RPC_STRING, &file, RPC_END);
577 n = readlink (file, buffer, 2048);
578 send_status (n, errno);
579 if (n >= 0) {
580 buffer[n] = 0;
581 rpc_send (msock, RPC_STRING, buffer, RPC_END);
583 g_free (file);
586 static void
587 do_unlink (void)
589 char *file;
590 int status;
592 rpc_get (msock, RPC_STRING, &file, RPC_END);
593 status = unlink (file);
594 send_status (status, errno);
595 g_free (file);
598 static void
599 do_rename (void)
601 char *f1, *f2;
602 int status;
604 rpc_get (msock, RPC_STRING, &f1, RPC_STRING, &f2, RPC_END);
605 status = rename (f1, f2);
606 send_status (status, errno);
607 g_free (f1);
608 g_free (f2);
611 static void
612 do_symlink (void)
614 char *f1, *f2;
615 int status;
617 rpc_get (msock, RPC_STRING, &f1, RPC_STRING, &f2, RPC_END);
618 status = symlink (f1, f2);
619 send_status (status, errno);
620 g_free (f1);
621 g_free (f2);
624 static void
625 do_link (void)
627 char *f1, *f2;
628 int status;
630 rpc_get (msock, RPC_STRING, &f1, RPC_STRING, &f2, RPC_END);
631 status = link (f1, f2);
632 send_status (status, errno);
633 g_free (f1);
634 g_free (f2);
638 /* }}} */
640 /* {{{ Misc commands */
642 static void
643 do_gethome (void)
645 rpc_send (msock, RPC_STRING, (home_dir) ? home_dir : "/", RPC_END);
648 static void
649 do_getupdir (void)
651 rpc_send (msock, RPC_STRING, (up_dir) ? up_dir : "/", RPC_END);
654 static void
655 do_chmod (void)
657 char *file;
658 int mode, status;
660 rpc_get (msock, RPC_STRING, &file, RPC_INT, &mode, RPC_END);
661 status = chmod (file, mode);
662 send_status (status, errno);
663 g_free (file);
666 static void
667 do_chown (void)
669 char *file;
670 int owner, group, status;
672 rpc_get (msock, RPC_STRING, &file, RPC_INT, &owner, RPC_INT, &group,
673 RPC_END);
674 status = chown (file, owner, group);
675 send_status (status, errno);
676 g_free (file);
679 static void
680 do_utime (void)
682 char *file;
683 int status;
684 long atime;
685 long mtime;
686 char *as;
687 char *ms;
688 struct utimbuf times;
690 rpc_get (msock, RPC_STRING, &file,
691 RPC_STRING, &as, RPC_STRING, &ms, RPC_END);
692 sscanf (as, "%lx", &atime);
693 sscanf (ms, "%lx", &mtime);
694 if (verbose)
695 printf ("Got a = %s, m = %s, comp a = %ld, m = %ld\n",
696 as, ms, atime, mtime);
697 g_free (as);
698 g_free (ms);
699 times.actime = (time_t) atime;
700 times.modtime = (time_t) mtime;
701 status = utime (file, &times);
702 send_status (status, errno);
703 g_free (file);
706 static void
707 do_quit (void)
709 quit_server = 1;
712 #ifdef HAVE_PAM
714 struct user_pass {
715 char *username;
716 char *password;
719 static int
720 mc_pam_conversation (int messages, const struct pam_message **msg,
721 struct pam_response **resp, void *appdata_ptr)
723 struct pam_response *r;
724 struct user_pass *up = appdata_ptr;
725 int status;
727 r = (struct pam_response *) malloc (sizeof (struct pam_response) *
728 messages);
729 if (!r)
730 return PAM_CONV_ERR;
731 *resp = r;
733 for (status = PAM_SUCCESS; messages--; msg++, r++) {
734 switch ((*msg)->msg_style) {
736 case PAM_PROMPT_ECHO_ON:
737 r->resp = strdup (up->username);
738 r->resp_retcode = PAM_SUCCESS;
739 break;
741 case PAM_PROMPT_ECHO_OFF:
742 r->resp = strdup (up->password);
743 r->resp_retcode = PAM_SUCCESS;
744 break;
746 case PAM_ERROR_MSG:
747 r->resp = NULL;
748 r->resp_retcode = PAM_SUCCESS;
749 break;
751 case PAM_TEXT_INFO:
752 r->resp = NULL;
753 r->resp_retcode = PAM_SUCCESS;
754 break;
757 return status;
760 static struct pam_conv conv = { &mc_pam_conversation, NULL };
763 /* Return 0 if authentication failed, 1 otherwise */
764 static int
765 mc_pam_auth (char *username, char *password)
767 pam_handle_t *pamh;
768 struct user_pass up;
769 int status;
771 up.username = username;
772 up.password = password;
773 conv.appdata_ptr = &up;
775 if ((status =
776 pam_start ("mcserv", username, &conv, &pamh)) != PAM_SUCCESS)
777 goto failed_pam;
778 if ((status = pam_authenticate (pamh, 0)) != PAM_SUCCESS)
779 goto failed_pam;
780 if ((status = pam_acct_mgmt (pamh, 0)) != PAM_SUCCESS)
781 goto failed_pam;
782 if ((status = pam_setcred (pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
783 goto failed_pam;
784 pam_end (pamh, status);
785 return 0;
787 failed_pam:
788 pam_end (pamh, status);
789 return 1;
792 #else /* Code for non-PAM authentication */
794 /* Keep reading until we find a \n */
795 static int
796 next_line (int socket)
798 char c;
800 while (1) {
801 if (read (socket, &c, 1) <= 0)
802 return 0;
803 if (c == '\n')
804 return 1;
808 static int
809 ftp_answer (int sock, char *text)
811 char answer[4];
813 next_line (sock);
814 socket_read_block (sock, answer, 3);
815 answer[3] = 0;
816 if (strcmp (answer, text) == 0)
817 return 1;
818 return 0;
821 static int
822 send_string (int sock, char *string)
824 return socket_write_block (sock, string, strlen (string));
827 static int
828 do_ftp_auth (char *username, char *password)
830 struct sockaddr_in local_address;
831 unsigned long inaddr;
832 int my_socket;
833 char answer[4];
835 memset ((char *) &local_address, 0, sizeof (local_address));
837 local_address.sin_family = AF_INET;
838 /* FIXME: extract the ftp port with the proper function */
839 local_address.sin_port = htons (21);
841 /* Convert localhost to usable format */
842 if ((inaddr = inet_addr ("127.0.0.1")) != -1)
843 memcpy ((char *) &local_address.sin_addr, (char *) &inaddr,
844 sizeof (inaddr));
846 if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
847 if (!isDaemon)
848 fprintf (stderr, "do_auth: can't create socket\n");
849 return 0;
851 if (connect (my_socket, (struct sockaddr *) &local_address,
852 sizeof (local_address)) < 0) {
853 fprintf (stderr,
854 "do_auth: can't connect to ftp daemon for authentication\n");
855 close (my_socket);
856 return 0;
858 send_string (my_socket, "user ");
859 send_string (my_socket, username);
860 send_string (my_socket, "\r\n");
861 if (!ftp_answer (my_socket, "331")) {
862 send_string (my_socket, "quit\r\n");
863 close (my_socket);
864 return 0;
866 next_line (my_socket); /* Eat all the line */
867 send_string (my_socket, "pass ");
868 send_string (my_socket, password);
869 send_string (my_socket, "\r\n");
870 socket_read_block (my_socket, answer, 3);
871 answer[3] = 0;
872 send_string (my_socket, "\r\n");
873 send_string (my_socket, "quit\r\n");
874 close (my_socket);
875 if (strcmp (answer, "230") == 0)
876 return 1;
877 return 0;
880 static int
881 do_classic_auth (char *username, char *password)
883 struct passwd *this;
884 int ret;
886 if ((this = getpwnam (username)) == 0)
887 return 0;
889 #ifdef HAVE_CRYPT
890 if (strcmp (crypt (password, this->pw_passwd), this->pw_passwd) == 0) {
891 ret = 1;
892 } else
893 #endif
895 ret = 0;
897 endpwent ();
898 return ret;
900 #endif /* non-PAM authentication */
902 /* Try to authenticate the user based on:
903 - PAM if the system has it, else it checks:
904 - pwdauth if the system supports it.
905 - conventional auth (check salt on /etc/passwd, crypt, and compare
906 - try to contact the local ftp server and login (if -f flag used)
908 static int
909 do_auth (char *username, char *password)
911 int auth = 0;
912 struct passwd *this;
914 if (strcmp (username, "anonymous") == 0)
915 username = "ftp";
917 #ifdef HAVE_PAM
918 if (mc_pam_auth (username, password) == 0)
919 auth = 1;
920 #else /* if there is no pam */
921 #ifdef HAVE_PWDAUTH
922 if (pwdauth (username, password) == 0)
923 auth = 1;
924 else
925 #endif
926 if (do_classic_auth (username, password))
927 auth = 1;
928 else if (ftp)
929 auth = do_ftp_auth (username, password);
930 #endif /* not pam */
932 if (!auth)
933 return 0;
935 this = getpwnam (username);
936 if (this == 0)
937 return 0;
939 if (chdir (this->pw_dir) == -1)
940 return 0;
942 if (this->pw_dir[strlen (this->pw_dir) - 1] == '/')
943 home_dir = strdup (this->pw_dir);
944 else {
945 home_dir = malloc (strlen (this->pw_dir) + 2);
946 if (home_dir) {
947 strcpy (home_dir, this->pw_dir);
948 strcat (home_dir, "/");
949 } else
950 home_dir = "/";
954 if (setgid (this->pw_gid) == -1)
955 return 0;
957 #ifdef HAVE_INITGROUPS
958 #ifdef NGROUPS_MAX
959 if (NGROUPS_MAX > 1 && initgroups (this->pw_name, this->pw_gid))
960 return 0;
961 #endif
962 #endif
964 #if defined (HAVE_SETUID)
965 if (setuid (this->pw_uid))
966 return 0;
967 #elif defined (HAVE_SETREUID)
968 if (setreuid (this->pw_uid, this->pw_uid))
969 return 0;
970 #endif
972 /* If the setuid call failed, then deny access */
973 /* This should fix the problem on those machines with strange setups */
974 if (getuid () != this->pw_uid)
975 return 0;
977 if (strcmp (username, "ftp") == 0)
978 chroot (this->pw_dir);
980 endpwent ();
981 return auth;
984 #if 0
985 static int
986 do_rauth (int socket)
988 struct sockaddr_in from;
989 struct hostent *hp;
991 if (getpeername (0, (struct sockaddr *) &from, &fromlen) < 0)
992 return 0;
993 from.sin_port = ntohs ((unsigned short) from.sin_port);
995 /* Strange, this should not happend */
996 if (from.sin_family != AF_INET)
997 return 0;
999 hp = gethostbyaddr ((char *) &fromp.sin_addr, sizeof (struct in_addr),
1000 fromp.sin_family);
1003 #endif
1005 static int
1006 do_rauth (int msock)
1008 return 0;
1011 static void
1012 login_reply (int logged_in)
1014 rpc_send (msock, RPC_INT,
1015 logged_in ? MC_LOGINOK : MC_INVALID_PASS, RPC_END);
1018 /* FIXME: Implement the anonymous login */
1019 static void
1020 do_login (void)
1022 char *username;
1023 char *password;
1024 int result;
1026 rpc_get (msock, RPC_LIMITED_STRING, &up_dir, RPC_LIMITED_STRING,
1027 &username, RPC_END);
1028 if (verbose)
1029 printf ("username: %s\n", username);
1031 if (r_auth) {
1032 logged_in = do_rauth (msock);
1033 if (logged_in) {
1034 login_reply (logged_in);
1035 return;
1038 rpc_send (msock, RPC_INT, MC_NEED_PASSWORD, RPC_END);
1039 rpc_get (msock, RPC_INT, &result, RPC_END);
1040 if (result == MC_QUIT)
1041 DO_QUIT_VOID ();
1042 if (result != MC_PASS) {
1043 if (verbose)
1044 printf ("do_login: Unknown response: %d\n", result);
1045 DO_QUIT_VOID ();
1047 rpc_get (msock, RPC_LIMITED_STRING, &password, RPC_END);
1048 logged_in = do_auth (username, password);
1049 endpwent ();
1050 login_reply (logged_in);
1053 /* }}} */
1055 /* {{{ Server and dispatching functions */
1057 /* This structure must be kept in synch with mcfs.h enums */
1059 static struct _command {
1060 char *command;
1061 void (*callback) (void);
1062 } commands[] = {
1064 "open", do_open}, {
1065 "close", do_close}, {
1066 "read", do_read}, {
1067 "write", do_write}, {
1068 "opendir", do_opendir}, {
1069 "readdir", do_readdir}, {
1070 "closedir", do_closedir}, {
1071 "stat ", do_stat}, {
1072 "lstat ", do_lstat}, {
1073 "fstat", do_fstat}, {
1074 "chmod", do_chmod}, {
1075 "chown", do_chown}, {
1076 "readlink ", do_readlink}, {
1077 "unlink", do_unlink}, {
1078 "rename", do_rename}, {
1079 "chdir ", do_chdir}, {
1080 "lseek", do_lseek}, {
1081 "rmdir", do_rmdir}, {
1082 "symlink", do_symlink}, {
1083 "mknod", do_mknod}, {
1084 "mkdir", do_mkdir}, {
1085 "link", do_link}, {
1086 "gethome", do_gethome}, {
1087 "getupdir", do_getupdir}, {
1088 "login", do_login}, {
1089 "quit", do_quit}, {
1090 "utime", do_utime},};
1092 static int ncommands = sizeof (commands) / sizeof (struct _command);
1094 static void
1095 exec_command (int command)
1097 if (command < 0 ||
1098 command >= ncommands || commands[command].command == 0) {
1099 fprintf (stderr, "Got unknown command: %d\n", command);
1100 DO_QUIT_VOID ();
1102 if (verbose)
1103 printf ("Command: %s\n", commands[command].command);
1104 (*commands[command].callback) ();
1107 static void
1108 check_version (void)
1110 int version;
1112 rpc_get (msock, RPC_INT, &version, RPC_END);
1113 if (version >= 1 && version <= RPC_PROGVER)
1114 rpc_send (msock, RPC_INT, MC_VERSION_OK, RPC_END);
1115 else
1116 rpc_send (msock, RPC_INT, MC_VERSION_MISMATCH, RPC_END);
1118 clnt_version = version;
1121 /* This routine is called by rpc_get/rpc_send when the connection is closed */
1122 void
1123 tcp_invalidate_socket (int sock)
1125 if (verbose)
1126 printf ("Connection closed\n");
1127 DO_QUIT_VOID ();
1130 static void
1131 server (int sock)
1133 int command;
1135 msock = sock;
1136 quit_server = 0;
1138 check_version ();
1139 do {
1140 if (rpc_get (sock, RPC_INT, &command, RPC_END) &&
1141 (logged_in || command == MC_LOGIN))
1142 exec_command (command);
1143 } while (!quit_server);
1146 /* }}} */
1148 /* {{{ Net support code */
1150 static char *
1151 get_client (int portnum)
1153 int sock, clilen, newsocket;
1154 struct sockaddr_in client_address, server_address;
1155 int yes = 1;
1157 if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
1158 return "Cannot create socket";
1160 /* Use this to debug: */
1161 if (setsockopt
1162 (sock, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof (yes)) < 0)
1163 return "setsockopt failed";
1165 memset ((char *) &server_address, 0, sizeof (server_address));
1166 server_address.sin_family = AF_INET;
1167 server_address.sin_addr.s_addr = htonl (INADDR_ANY);
1168 server_address.sin_port = htons (portnum);
1170 if (bind (sock, (struct sockaddr *) &server_address,
1171 sizeof (server_address)) < 0)
1172 return "Cannot bind";
1174 listen (sock, 5);
1176 for (;;) {
1177 int child;
1179 clilen = sizeof (client_address);
1180 newsocket = accept (sock, (struct sockaddr *) &client_address,
1181 &clilen);
1183 if (isDaemon && (child = fork ())) {
1184 int status;
1186 close (newsocket);
1187 waitpid (child, &status, 0);
1188 continue;
1191 if (isDaemon && fork ())
1192 exit (0);
1194 server (newsocket);
1195 close (newsocket);
1196 return 0;
1200 #ifdef HAVE_PMAP_SET
1201 static void
1202 signal_int_handler (int sig)
1204 pmap_unset (RPC_PROGNUM, RPC_PROGVER);
1206 #endif
1208 #ifndef IPPORT_RESERVED
1209 #define IPPORT_RESERVED 1024
1210 #endif
1212 static int
1213 get_port_number (void)
1215 int port = 0;
1217 #ifdef HAVE_RRESVPORT
1218 int start_port = IPPORT_RESERVED;
1220 port = rresvport (&start_port);
1221 if (port == -1) {
1222 if (geteuid () == 0) {
1223 fprintf (stderr,
1224 "Could not bind the server on a reserved port\n");
1225 DO_QUIT_NONVOID (-1);
1227 port = 0;
1229 #endif
1230 if (port)
1231 return port;
1233 port = mcserver_port;
1235 return port;
1238 static void
1239 register_port (int portnum, int abort_if_fail)
1241 #ifdef HAVE_PMAP_SET
1242 /* Register our service with the portmapper */
1243 /* protocol: pmap_set (prognum, versnum, protocol, portp) */
1245 if (pmap_set (RPC_PROGNUM, RPC_PROGVER, IPPROTO_TCP, portnum))
1246 signal (SIGINT, signal_int_handler);
1247 else {
1248 fprintf (stderr, "Could not register service with portmapper\n");
1249 if (abort_if_fail)
1250 exit (1);
1252 #else
1253 if (abort_if_fail) {
1254 fprintf (stderr,
1255 "This system lacks port registration, try using the -p\n"
1256 "flag to force installation at a given port");
1258 #endif
1261 /* }}} */
1264 main (int argc, char *argv[])
1266 char *result;
1267 extern char *optarg;
1268 int c;
1270 while ((c = getopt (argc, argv, "fdiqp:v")) != -1) {
1271 switch (c) {
1272 case 'd':
1273 isDaemon = 1;
1274 verbose = 0;
1275 break;
1277 case 'v':
1278 verbose = 1;
1279 break;
1281 case 'f':
1282 ftp = 1;
1283 break;
1285 case 'q':
1286 verbose = 0;
1287 break;
1289 case 'p':
1290 portnum = atoi (optarg);
1291 break;
1293 case 'i':
1294 inetd_started = 1;
1295 break;
1297 case 'r':
1298 r_auth = 1;
1299 break;
1301 default:
1302 fprintf (stderr, "Usage is: mcserv [options] [-p portnum]\n\n"
1303 "options are:\n"
1304 "-d become a daemon (sets -q)\n" "-q quiet mode\n"
1305 /* "-r use rhost based authentication\n" */
1306 #ifndef HAVE_PAM
1307 "-f force ftp authentication\n"
1308 #endif
1309 "-v verbose mode\n"
1310 "-p to specify a port number to listen\n");
1311 exit (0);
1316 if (isDaemon && fork ())
1317 exit (0);
1319 if (portnum == 0)
1320 portnum = get_port_number ();
1322 if (portnum != -1) {
1323 register_port (portnum, 0);
1324 if (verbose)
1325 printf ("Using port %d\n", portnum);
1326 if ((result = get_client (portnum)))
1327 perror (result);
1328 #ifdef HAVE_PMAP_SET
1329 if (!isDaemon)
1330 pmap_unset (RPC_PROGNUM, RPC_PROGVER);
1331 #endif
1333 exit (return_code);
1336 /* FIXME: This function should not be used in mcserv */
1337 void
1338 vfs_die (char *m)
1340 fputs (m, stderr);
1341 exit (1);