Corrected Changelog entries for ri-gcc-warnings.m4
[midnight-commander.git] / vfs / mcserv.c
blob1c5e57b23373e28cb8d13efbc02314ab6734c5df
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>
58 #ifdef HAVE_GETOPT_H
59 # include <getopt.h>
60 #endif
62 /* Network include files */
63 #include <sys/socket.h>
64 #include <netinet/in.h>
65 #include <netdb.h>
66 #ifdef HAVE_ARPA_INET_H
67 #include <arpa/inet.h>
68 #endif
69 #ifdef HAVE_PMAP_SET
70 # include <rpc/rpc.h>
71 # include <rpc/pmap_prot.h>
72 # ifdef HAVE_RPC_PMAP_CLNT_H
73 # include <rpc/pmap_clnt.h>
74 # endif
75 #endif
77 #if defined(HAVE_PAM)
78 # if !defined(HAVE_SECURITY_PAM_MISC_H)
79 # undef HAVE_PAM
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 #else
91 #endif /* !HAVE_PAM */
93 #ifdef HAVE_CRYPT_H
94 # include <crypt.h>
95 #endif /* !HAVE_CRYPT_H */
97 #ifdef HAVE_SHADOW_H
98 # include <shadow.h>
99 #else
100 # ifdef HAVE_SHADOW_SHADOW_H
101 # include <shadow/shadow.h>
102 # endif
103 #endif
105 #include "utilvfs.h"
107 #include "vfs.h"
108 #include "mcfs.h"
109 #include "mcfsutil.h"
110 #include "tcputil.h"
112 /* replacement for g_free() from glib */
113 #undef g_free
114 #define g_free(x) do {if (x) free (x);} while (0)
116 /* We don't care about SIGPIPE */
117 int got_sigpipe = 0;
119 /* The socket from which we accept commands */
120 int msock;
122 /* Requested version number from client */
123 static int clnt_version;
125 /* If non zero, we accept further commands */
126 int logged_in = 0;
128 /* Home directory */
129 char *home_dir = NULL;
131 char *up_dir = NULL;
133 /* Were we started from inetd? */
134 int inetd_started = 0;
136 /* Are we running as a daemon? */
137 int isDaemon = 0;
139 /* guess */
140 int verbose = 0;
142 /* ftp auth */
143 int ftp = 0;
145 /* port number in which we listen to connections,
146 * if zero, we try to contact the portmapper to get a port, and
147 * if it's not possible, then we use a hardcoded value
149 int portnum = 0;
151 /* if the server will use rcmd based authentication (hosts.equiv .rhosts) */
152 int r_auth = 0;
154 #define OPENDIR_HANDLES 8
156 #define DO_QUIT_VOID() \
157 do { \
158 quit_server = 1; \
159 return_code = 1; \
160 return; \
161 } while (0)
163 /* Only used by get_port_number */
164 #define DO_QUIT_NONVOID(a) \
165 do { \
166 quit_server = 1; \
167 return_code = 1; \
168 return (a); \
169 } while (0)
171 char buffer[4096];
172 int debug = 1;
173 static int quit_server;
174 static int return_code;
176 /* }}} */
178 /* {{{ Misc routines */
180 static void
181 send_status (int status, int errno_number)
183 rpc_send (msock, RPC_INT, status, RPC_INT, errno_number, RPC_END);
184 errno = 0;
187 /* }}} */
189 /* {{{ File with handle operations */
191 static void
192 do_open (void)
194 int handle, flags, mode;
195 char *arg;
197 rpc_get (msock, RPC_STRING, &arg, RPC_INT, &flags, RPC_INT, &mode,
198 RPC_END);
200 handle = open (arg, flags, mode);
201 send_status (handle, errno);
202 g_free (arg);
205 static void
206 do_read (void)
208 int handle, count, n;
209 void *data;
211 rpc_get (msock, RPC_INT, &handle, RPC_INT, &count, RPC_END);
212 data = malloc (count);
213 if (!data) {
214 send_status (-1, ENOMEM);
215 return;
217 if (verbose)
218 printf ("count=%d\n", count);
219 n = read (handle, data, count);
220 if (verbose)
221 printf ("result=%d\n", n);
222 if (n < 0) {
223 send_status (-1, errno);
224 return;
226 send_status (n, 0);
227 rpc_send (msock, RPC_BLOCK, n, data, RPC_END);
229 g_free (data);
232 static void
233 do_write (void)
235 int handle, count, status, written = 0;
236 char buf[8192];
238 rpc_get (msock, RPC_INT, &handle, RPC_INT, &count, RPC_END);
239 status = 0;
240 while (count) {
241 int nbytes = count > 8192 ? 8192 : count;
243 rpc_get (msock, RPC_BLOCK, nbytes, buf, RPC_END);
244 status = write (handle, buf, nbytes);
245 if (status < 0) {
246 send_status (status, errno);
247 return;
249 /* FIXED: amount written must be returned to caller */
250 written += status;
251 if (status < nbytes) {
252 send_status (written, errno);
253 return;
255 count -= nbytes;
257 send_status (written, errno);
260 static void
261 do_lseek (void)
263 int handle, offset, whence, status;
265 rpc_get (msock, RPC_INT, &handle, RPC_INT, &offset, RPC_INT, &whence,
266 RPC_END);
267 status = lseek (handle, offset, whence);
268 send_status (status, errno);
271 static void
272 do_close (void)
274 int handle, status;
276 rpc_get (msock, RPC_INT, &handle, RPC_END);
277 status = close (handle);
278 send_status (status, errno);
281 /* }}} */
283 /* {{{ Stat family routines */
285 static void
286 send_time (int sock, time_t time)
288 if (clnt_version == 1) {
289 char *ct;
290 int month;
292 ct = ctime (&time);
293 ct[3] = ct[10] = ct[13] = ct[16] = ct[19] = 0;
295 /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
296 if (ct[4] == 'J') {
297 if (ct[5] == 'a') {
298 month = 0;
299 } else
300 month = (ct[6] == 'n') ? 5 : 6;
301 } else if (ct[4] == 'F') {
302 month = 1;
303 } else if (ct[4] == 'M') {
304 month = (ct[6] == 'r') ? 2 : 5;
305 } else if (ct[4] == 'A') {
306 month = (ct[5] == 'p') ? 3 : 7;
307 } else if (ct[4] == 'S') {
308 month = 8;
309 } else if (ct[4] == 'O') {
310 month = 9;
311 } else if (ct[4] == 'N') {
312 month = 10;
313 } else
314 month = 11;
315 rpc_send (msock, RPC_INT, atoi (&ct[17]), /* sec */
316 RPC_INT, atoi (&ct[14]), /* min */
317 RPC_INT, atoi (&ct[11]), /* hour */
318 RPC_INT, atoi (&ct[8]), /* mday */
319 RPC_INT, atoi (&ct[20]), /* year */
320 RPC_INT, month, /* month */
321 RPC_END);
322 } else {
323 long ltime = (long) time;
324 char buf[BUF_SMALL];
326 snprintf (buf, sizeof (buf), "%lx", ltime);
327 rpc_send (msock, RPC_STRING, buf, RPC_END);
331 static void
332 send_stat_info (struct stat *st)
334 long mylong;
335 int blocks =
336 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
337 st->st_blocks;
338 #else
339 st->st_size / 1024;
340 #endif
342 #ifdef HAVE_STRUCT_STAT_ST_RDEV
343 mylong = st->st_rdev;
344 #else
345 mylong = 0;
346 #endif
347 rpc_send (msock, RPC_INT, (long) mylong, RPC_INT, (long) st->st_ino,
348 RPC_INT, (long) st->st_mode, RPC_INT, (long) st->st_nlink,
349 RPC_INT, (long) st->st_uid, RPC_INT, (long) st->st_gid,
350 RPC_INT, (long) st->st_size, RPC_INT, (long) blocks,
351 RPC_END);
352 send_time (msock, st->st_atime);
353 send_time (msock, st->st_mtime);
354 send_time (msock, st->st_ctime);
357 static void
358 do_lstat (void)
360 struct stat st;
361 char *file;
362 int n;
364 rpc_get (msock, RPC_STRING, &file, RPC_END);
365 n = lstat (file, &st);
366 send_status (n, errno);
367 if (n >= 0)
368 send_stat_info (&st);
369 g_free (file);
372 static void
373 do_fstat (void)
375 int handle;
376 int n;
377 struct stat st;
379 rpc_get (msock, RPC_INT, &handle, RPC_END);
380 n = fstat (handle, &st);
381 send_status (n, errno);
382 if (n < 0)
383 return;
385 send_stat_info (&st);
388 static void
389 do_stat (void)
391 struct stat st;
392 int n;
393 char *file;
395 rpc_get (msock, RPC_STRING, &file, RPC_END);
397 n = stat (file, &st);
398 send_status (n, errno);
399 if (n >= 0)
400 send_stat_info (&st);
401 g_free (file);
404 /* }}} */
406 /* {{{ Directory lookup operations */
408 static struct {
409 int used;
410 DIR *dirs[OPENDIR_HANDLES];
411 char *names[OPENDIR_HANDLES];
412 } mcfs_DIR;
414 static void
415 close_handle (int handle)
417 if (mcfs_DIR.used > 0)
418 mcfs_DIR.used--;
419 if (mcfs_DIR.dirs[handle])
420 closedir (mcfs_DIR.dirs[handle]);
421 if (mcfs_DIR.names[handle])
422 g_free (mcfs_DIR.names[handle]);
423 mcfs_DIR.dirs[handle] = 0;
424 mcfs_DIR.names[handle] = 0;
427 static void
428 do_opendir (void)
430 int handle, i;
431 char *arg;
432 DIR *p;
434 rpc_get (msock, RPC_STRING, &arg, RPC_END);
436 if (mcfs_DIR.used == OPENDIR_HANDLES) {
437 send_status (-1, ENFILE); /* Error */
438 g_free (arg);
439 return;
442 handle = -1;
443 for (i = 0; i < OPENDIR_HANDLES; i++) {
444 if (mcfs_DIR.dirs[i] == 0) {
445 handle = i;
446 break;
450 if (handle == -1) {
451 send_status (-1, EMFILE);
452 g_free (arg);
453 if (!inetd_started)
454 fprintf (stderr,
455 "OOPS! you have found a bug in mc - do_opendir()!\n");
456 return;
459 if (verbose)
460 printf ("handle=%d\n", handle);
461 p = opendir (arg);
462 if (p) {
463 mcfs_DIR.dirs[handle] = p;
464 mcfs_DIR.names[handle] = arg;
465 mcfs_DIR.used++;
467 /* Because 0 is an error value */
468 rpc_send (msock, RPC_INT, handle + 1, RPC_INT, 0, RPC_END);
470 } else {
471 send_status (-1, errno);
472 g_free (arg);
476 /* Sends the complete directory listing, as well as the stat information */
477 static void
478 do_readdir (void)
480 struct dirent *dirent;
481 struct stat st;
482 int handle, n;
484 rpc_get (msock, RPC_INT, &handle, RPC_END);
486 if (!handle) {
487 rpc_send (msock, RPC_INT, 0, RPC_END);
488 return;
491 /* We incremented it in opendir */
492 handle--;
494 while ((dirent = readdir (mcfs_DIR.dirs[handle]))) {
495 int fname_len;
496 char *fname;
497 int length = NLENGTH (dirent);
499 rpc_send (msock, RPC_INT, length, RPC_END);
500 rpc_send (msock, RPC_BLOCK, length, dirent->d_name, RPC_END);
501 fname_len =
502 strlen (mcfs_DIR.names[handle]) + strlen (dirent->d_name) + 2;
503 fname = malloc (fname_len);
504 snprintf (fname, fname_len, "%s/%s", mcfs_DIR.names[handle],
505 dirent->d_name);
506 n = lstat (fname, &st);
507 g_free (fname);
508 send_status (n, errno);
509 if (n >= 0)
510 send_stat_info (&st);
512 rpc_send (msock, RPC_INT, 0, RPC_END);
515 static void
516 do_closedir (void)
518 int handle;
520 rpc_get (msock, RPC_INT, &handle, RPC_END);
521 close_handle (handle - 1);
524 /* }}} */
526 /* {{{ Operations with one and two file name argument */
528 static void
529 do_chdir (void)
531 char *file;
532 int status;
534 rpc_get (msock, RPC_STRING, &file, RPC_END);
536 status = chdir (file);
537 send_status (status, errno);
538 g_free (file);
541 static void
542 do_rmdir (void)
544 char *file;
545 int status;
547 rpc_get (msock, RPC_STRING, &file, RPC_END);
549 status = rmdir (file);
550 send_status (status, errno);
551 g_free (file);
554 static void
555 do_mkdir (void)
557 char *file;
558 int mode, status;
560 rpc_get (msock, RPC_STRING, &file, RPC_INT, &mode, RPC_END);
562 status = mkdir (file, mode);
563 send_status (status, errno);
564 g_free (file);
567 static void
568 do_mknod (void)
570 char *file;
571 int mode, dev, status;
573 rpc_get (msock, RPC_STRING, &file, RPC_INT, &mode, RPC_INT, &dev,
574 RPC_END);
576 status = mknod (file, mode, dev);
577 send_status (status, errno);
578 g_free (file);
581 static void
582 do_readlink (void)
584 char buffer[2048];
585 char *file;
586 int n;
588 rpc_get (msock, RPC_STRING, &file, RPC_END);
589 n = readlink (file, buffer, 2048 - 1);
590 send_status (n, errno);
591 if (n >= 0) {
592 buffer[n] = 0;
593 rpc_send (msock, RPC_STRING, buffer, RPC_END);
595 g_free (file);
598 static void
599 do_unlink (void)
601 char *file;
602 int status;
604 rpc_get (msock, RPC_STRING, &file, RPC_END);
605 status = unlink (file);
606 send_status (status, errno);
607 g_free (file);
610 static void
611 do_rename (void)
613 char *f1, *f2;
614 int status;
616 rpc_get (msock, RPC_STRING, &f1, RPC_STRING, &f2, RPC_END);
617 status = rename (f1, f2);
618 send_status (status, errno);
619 g_free (f1);
620 g_free (f2);
623 static void
624 do_symlink (void)
626 char *f1, *f2;
627 int status;
629 rpc_get (msock, RPC_STRING, &f1, RPC_STRING, &f2, RPC_END);
630 status = symlink (f1, f2);
631 send_status (status, errno);
632 g_free (f1);
633 g_free (f2);
636 static void
637 do_link (void)
639 char *f1, *f2;
640 int status;
642 rpc_get (msock, RPC_STRING, &f1, RPC_STRING, &f2, RPC_END);
643 status = link (f1, f2);
644 send_status (status, errno);
645 g_free (f1);
646 g_free (f2);
650 /* }}} */
652 /* {{{ Misc commands */
654 static void
655 do_gethome (void)
657 rpc_send (msock, RPC_STRING, (home_dir) ? home_dir : "/", RPC_END);
660 static void
661 do_getupdir (void)
663 rpc_send (msock, RPC_STRING, (up_dir) ? up_dir : "/", RPC_END);
666 static void
667 do_chmod (void)
669 char *file;
670 int mode, status;
672 rpc_get (msock, RPC_STRING, &file, RPC_INT, &mode, RPC_END);
673 status = chmod (file, mode);
674 send_status (status, errno);
675 g_free (file);
678 static void
679 do_chown (void)
681 char *file;
682 int owner, group, status;
684 rpc_get (msock, RPC_STRING, &file, RPC_INT, &owner, RPC_INT, &group,
685 RPC_END);
686 status = chown (file, owner, group);
687 send_status (status, errno);
688 g_free (file);
691 static void
692 do_utime (void)
694 char *file;
695 int status;
696 long atime;
697 long mtime;
698 char *as;
699 char *ms;
700 struct utimbuf times;
702 rpc_get (msock, RPC_STRING, &file, RPC_STRING, &as, RPC_STRING, &ms,
703 RPC_END);
704 sscanf (as, "%lx", &atime);
705 sscanf (ms, "%lx", &mtime);
706 if (verbose)
707 printf ("Got a = %s, m = %s, comp a = %ld, m = %ld\n", as, ms,
708 atime, mtime);
709 g_free (as);
710 g_free (ms);
711 times.actime = (time_t) atime;
712 times.modtime = (time_t) mtime;
713 status = utime (file, &times);
714 send_status (status, errno);
715 g_free (file);
718 static void
719 do_quit (void)
721 quit_server = 1;
724 #ifdef HAVE_PAM
726 struct user_pass {
727 char *username;
728 char *password;
731 static int
732 mc_pam_conversation (int messages, const struct pam_message **msg,
733 struct pam_response **resp, void *appdata_ptr)
735 struct pam_response *r;
736 struct user_pass *up = appdata_ptr;
737 int status;
739 r = (struct pam_response *) malloc (sizeof (struct pam_response) *
740 messages);
741 if (!r)
742 return PAM_CONV_ERR;
743 *resp = r;
745 for (status = PAM_SUCCESS; messages--; msg++, r++) {
746 switch ((*msg)->msg_style) {
748 case PAM_PROMPT_ECHO_ON:
749 r->resp = strdup (up->username);
750 r->resp_retcode = PAM_SUCCESS;
751 break;
753 case PAM_PROMPT_ECHO_OFF:
754 r->resp = strdup (up->password);
755 r->resp_retcode = PAM_SUCCESS;
756 break;
758 case PAM_ERROR_MSG:
759 r->resp = NULL;
760 r->resp_retcode = PAM_SUCCESS;
761 break;
763 case PAM_TEXT_INFO:
764 r->resp = NULL;
765 r->resp_retcode = PAM_SUCCESS;
766 break;
769 return status;
772 static struct pam_conv conv = { &mc_pam_conversation, NULL };
775 /* Return 0 if authentication failed, 1 otherwise */
776 static int
777 mc_pam_auth (char *username, char *password)
779 pam_handle_t *pamh;
780 struct user_pass up;
781 int status;
783 up.username = username;
784 up.password = password;
785 conv.appdata_ptr = &up;
787 if ((status =
788 pam_start ("mcserv", username, &conv, &pamh)) != PAM_SUCCESS)
789 goto failed_pam;
790 if ((status = pam_authenticate (pamh, 0)) != PAM_SUCCESS)
791 goto failed_pam;
792 if ((status = pam_acct_mgmt (pamh, 0)) != PAM_SUCCESS)
793 goto failed_pam;
794 if ((status = pam_setcred (pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
795 goto failed_pam;
796 pam_end (pamh, status);
797 return 0;
799 failed_pam:
800 pam_end (pamh, status);
801 return 1;
804 #else /* !HAVE_PAM */
806 /* Keep reading until we find a \n */
807 static int
808 next_line (int socket)
810 char c;
812 while (1) {
813 if (read (socket, &c, 1) <= 0)
814 return 0;
815 if (c == '\n')
816 return 1;
820 static int
821 ftp_answer (int sock, char *text)
823 char answer[4];
825 next_line (sock);
826 socket_read_block (sock, answer, 3);
827 answer[3] = 0;
828 if (strcmp (answer, text) == 0)
829 return 1;
830 return 0;
833 static int
834 send_string (int sock, char *string)
836 return socket_write_block (sock, string, strlen (string));
839 static int
840 do_ftp_auth (char *username, char *password)
842 struct sockaddr_in local_address;
843 unsigned long inaddr;
844 int my_socket;
845 char answer[4];
847 memset ((char *) &local_address, 0, sizeof (local_address));
849 local_address.sin_family = AF_INET;
850 /* FIXME: extract the ftp port with the proper function */
851 local_address.sin_port = htons (21);
853 /* Convert localhost to usable format */
854 if ((inaddr = inet_addr ("127.0.0.1")) != INADDR_NONE)
855 memcpy ((char *) &local_address.sin_addr, (char *) &inaddr,
856 sizeof (inaddr));
858 if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
859 if (!isDaemon)
860 fprintf (stderr, "do_auth: can't create socket\n");
861 return 0;
863 if (connect
864 (my_socket, (struct sockaddr *) &local_address,
865 sizeof (local_address)) < 0) {
866 fprintf (stderr,
867 "do_auth: can't connect to ftp daemon for authentication\n");
868 close (my_socket);
869 return 0;
871 send_string (my_socket, "user ");
872 send_string (my_socket, username);
873 send_string (my_socket, "\r\n");
874 if (!ftp_answer (my_socket, "331")) {
875 send_string (my_socket, "quit\r\n");
876 close (my_socket);
877 return 0;
879 next_line (my_socket); /* Eat all the line */
880 send_string (my_socket, "pass ");
881 send_string (my_socket, password);
882 send_string (my_socket, "\r\n");
883 socket_read_block (my_socket, answer, 3);
884 answer[3] = 0;
885 send_string (my_socket, "\r\n");
886 send_string (my_socket, "quit\r\n");
887 close (my_socket);
888 if (strcmp (answer, "230") == 0)
889 return 1;
890 return 0;
893 #ifdef HAVE_CRYPT
894 static int
895 do_classic_auth (char *username, char *password)
897 int ret = 0;
898 char *encr_pwd = NULL;
899 struct passwd *pw;
900 #ifdef HAVE_SHADOW
901 struct spwd *spw;
902 #endif
904 if ((pw = getpwnam (username)) == 0)
905 return 0;
907 #ifdef HAVE_SHADOW
908 setspent ();
910 /* Password expiration is not checked! */
911 if ((spw = getspnam (username)) == NULL)
912 encr_pwd = "*";
913 else
914 encr_pwd = spw->sp_pwdp;
916 endspent ();
917 #else
918 encr_pwd = pw->pw_passwd;
919 #endif
921 if (strcmp (crypt (password, encr_pwd), encr_pwd) == 0)
922 ret = 1;
924 endpwent ();
925 return ret;
927 #endif /* HAVE_CRYPT */
928 #endif /* !HAVE_PAM */
930 /* Try to authenticate the user based on:
931 - PAM if the system has it, else it checks:
932 - pwdauth if the system supports it.
933 - conventional auth (check salt on /etc/passwd, crypt, and compare
934 - try to contact the local ftp server and login (if -f flag used)
936 static int
937 do_auth (char *username, char *password)
939 int auth = 0;
940 struct passwd *this;
942 if (strcmp (username, "anonymous") == 0)
943 username = "ftp";
945 #ifdef HAVE_PAM
946 if (mc_pam_auth (username, password) == 0)
947 auth = 1;
948 #else /* if there is no pam */
949 #ifdef HAVE_PWDAUTH
950 if (pwdauth (username, password) == 0)
951 auth = 1;
952 else
953 #endif
954 #ifdef HAVE_CRYPT
955 if (do_classic_auth (username, password))
956 auth = 1;
957 else
958 #endif
959 if (ftp)
960 auth = do_ftp_auth (username, password);
961 #endif /* not pam */
963 if (!auth)
964 return 0;
966 this = getpwnam (username);
967 if (this == 0)
968 return 0;
970 if (chdir (this->pw_dir) == -1)
971 return 0;
973 if (this->pw_dir[strlen (this->pw_dir) - 1] == '/')
974 home_dir = strdup (this->pw_dir);
975 else {
976 home_dir = malloc (strlen (this->pw_dir) + 2);
977 if (home_dir) {
978 strcpy (home_dir, this->pw_dir);
979 strcat (home_dir, "/");
980 } else
981 home_dir = "/";
985 if (setgid (this->pw_gid) == -1)
986 return 0;
988 #ifdef HAVE_INITGROUPS
989 #ifdef NGROUPS_MAX
990 if (NGROUPS_MAX > 1 && initgroups (this->pw_name, this->pw_gid))
991 return 0;
992 #endif
993 #endif
995 #if defined (HAVE_SETUID)
996 if (setuid (this->pw_uid))
997 return 0;
998 #elif defined (HAVE_SETREUID)
999 if (setreuid (this->pw_uid, this->pw_uid))
1000 return 0;
1001 #endif
1003 /* If the setuid call failed, then deny access */
1004 /* This should fix the problem on those machines with strange setups */
1005 if (getuid () != this->pw_uid)
1006 return 0;
1008 if (strcmp (username, "ftp") == 0)
1009 chroot (this->pw_dir);
1011 endpwent ();
1012 return auth;
1015 #if 0
1016 static int
1017 do_rauth (int socket)
1019 struct sockaddr_in from;
1020 struct hostent *hp;
1022 if (getpeername (0, (struct sockaddr *) &from, &fromlen) < 0)
1023 return 0;
1024 from.sin_port = ntohs ((unsigned short) from.sin_port);
1026 /* Strange, this should not happend */
1027 if (from.sin_family != AF_INET)
1028 return 0;
1030 hp = gethostbyaddr ((char *) &fromp.sin_addr, sizeof (struct in_addr),
1031 fromp.sin_family);
1034 #endif
1036 static int
1037 do_rauth (int msock)
1039 return 0;
1042 static void
1043 login_reply (int logged_in)
1045 rpc_send (msock, RPC_INT, logged_in ? MC_LOGINOK : MC_INVALID_PASS,
1046 RPC_END);
1049 /* FIXME: Implement the anonymous login */
1050 static void
1051 do_login (void)
1053 char *username;
1054 char *password;
1055 int result;
1057 rpc_get (msock, RPC_LIMITED_STRING, &up_dir, RPC_LIMITED_STRING,
1058 &username, RPC_END);
1059 if (verbose)
1060 printf ("username: %s\n", username);
1062 if (r_auth) {
1063 logged_in = do_rauth (msock);
1064 if (logged_in) {
1065 login_reply (logged_in);
1066 return;
1069 rpc_send (msock, RPC_INT, MC_NEED_PASSWORD, RPC_END);
1070 rpc_get (msock, RPC_INT, &result, RPC_END);
1071 if (result == MC_QUIT)
1072 DO_QUIT_VOID ();
1073 if (result != MC_PASS) {
1074 if (verbose)
1075 printf ("do_login: Unknown response: %d\n", result);
1076 DO_QUIT_VOID ();
1078 rpc_get (msock, RPC_LIMITED_STRING, &password, RPC_END);
1079 logged_in = do_auth (username, password);
1080 endpwent ();
1081 login_reply (logged_in);
1084 /* }}} */
1086 /* {{{ Server and dispatching functions */
1088 /* This structure must be kept in synch with mcfs.h enums */
1090 static const struct _command {
1091 char *command;
1092 void (*callback) (void);
1093 } commands[] = {
1095 "open", do_open}, {
1096 "close", do_close}, {
1097 "read", do_read}, {
1098 "write", do_write}, {
1099 "opendir", do_opendir}, {
1100 "readdir", do_readdir}, {
1101 "closedir", do_closedir}, {
1102 "stat ", do_stat}, {
1103 "lstat ", do_lstat}, {
1104 "fstat", do_fstat}, {
1105 "chmod", do_chmod}, {
1106 "chown", do_chown}, {
1107 "readlink ", do_readlink}, {
1108 "unlink", do_unlink}, {
1109 "rename", do_rename}, {
1110 "chdir ", do_chdir}, {
1111 "lseek", do_lseek}, {
1112 "rmdir", do_rmdir}, {
1113 "symlink", do_symlink}, {
1114 "mknod", do_mknod}, {
1115 "mkdir", do_mkdir}, {
1116 "link", do_link}, {
1117 "gethome", do_gethome}, {
1118 "getupdir", do_getupdir}, {
1119 "login", do_login}, {
1120 "quit", do_quit}, {
1121 "utime", do_utime}};
1123 static int ncommands = sizeof (commands) / sizeof (struct _command);
1125 static void
1126 exec_command (int command)
1128 if (command < 0 || command >= ncommands
1129 || commands[command].command == 0) {
1130 fprintf (stderr, "Got unknown command: %d\n", command);
1131 DO_QUIT_VOID ();
1133 if (verbose)
1134 printf ("Command: %s\n", commands[command].command);
1135 (*commands[command].callback) ();
1138 static void
1139 check_version (void)
1141 int version;
1143 rpc_get (msock, RPC_INT, &version, RPC_END);
1144 if (version >= 1 && version <= RPC_PROGVER)
1145 rpc_send (msock, RPC_INT, MC_VERSION_OK, RPC_END);
1146 else
1147 rpc_send (msock, RPC_INT, MC_VERSION_MISMATCH, RPC_END);
1149 clnt_version = version;
1152 /* This routine is called by rpc_get/rpc_send when the connection is closed */
1153 void
1154 tcp_invalidate_socket (int sock)
1156 if (verbose)
1157 printf ("Connection closed\n");
1158 DO_QUIT_VOID ();
1161 static void
1162 server (int sock)
1164 int command;
1166 msock = sock;
1167 quit_server = 0;
1169 check_version ();
1170 do {
1171 if (rpc_get (sock, RPC_INT, &command, RPC_END)
1172 && (logged_in || command == MC_LOGIN))
1173 exec_command (command);
1174 } while (!quit_server);
1177 /* }}} */
1179 /* {{{ Net support code */
1181 static char *
1182 get_client (int portnum)
1184 int sock, clilen, newsocket;
1185 struct sockaddr_in client_address, server_address;
1186 int yes = 1;
1188 if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
1189 return "Cannot create socket";
1191 /* Use this to debug: */
1192 if (setsockopt
1193 (sock, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof (yes)) < 0)
1194 return "setsockopt failed";
1196 memset ((char *) &server_address, 0, sizeof (server_address));
1197 server_address.sin_family = AF_INET;
1198 server_address.sin_addr.s_addr = htonl (INADDR_ANY);
1199 server_address.sin_port = htons (portnum);
1201 if (bind
1202 (sock, (struct sockaddr *) &server_address,
1203 sizeof (server_address)) < 0)
1204 return "Cannot bind";
1206 listen (sock, 5);
1208 for (;;) {
1209 int child;
1211 clilen = sizeof (client_address);
1212 newsocket =
1213 accept (sock, (struct sockaddr *) &client_address, &clilen);
1215 if (isDaemon && (child = fork ())) {
1216 int status;
1218 close (newsocket);
1219 waitpid (child, &status, 0);
1220 continue;
1223 if (isDaemon && fork ())
1224 exit (0);
1226 server (newsocket);
1227 close (newsocket);
1228 return 0;
1232 #ifdef HAVE_PMAP_SET
1233 static void
1234 signal_int_handler (int sig)
1236 pmap_unset (RPC_PROGNUM, RPC_PROGVER);
1238 #endif
1240 #ifndef IPPORT_RESERVED
1241 #define IPPORT_RESERVED 1024
1242 #endif
1244 static int
1245 get_port_number (void)
1247 int port = 0;
1249 #ifdef HAVE_RRESVPORT
1250 int start_port = IPPORT_RESERVED;
1252 port = rresvport (&start_port);
1253 if (port == -1) {
1254 if (geteuid () == 0) {
1255 fprintf (stderr,
1256 "Cannot bind the server on a reserved port\n");
1257 DO_QUIT_NONVOID (-1);
1259 port = 0;
1261 #endif
1262 if (port)
1263 return port;
1265 port = mcserver_port;
1267 return port;
1270 static void
1271 register_port (int portnum, int abort_if_fail)
1273 #ifdef HAVE_PMAP_SET
1274 /* Register our service with the portmapper */
1275 /* protocol: pmap_set (prognum, versnum, protocol, portp) */
1277 if (pmap_set (RPC_PROGNUM, RPC_PROGVER, IPPROTO_TCP, portnum))
1278 signal (SIGINT, signal_int_handler);
1279 else {
1280 fprintf (stderr, "Cannot register service with portmapper\n");
1281 if (abort_if_fail)
1282 exit (1);
1284 #else
1285 if (abort_if_fail) {
1286 fprintf (stderr,
1287 "This system lacks port registration, try using the -p\n"
1288 "flag to force installation at a given port");
1290 #endif
1293 /* }}} */
1296 main (int argc, char *argv[])
1298 char *result;
1299 int c;
1301 while ((c = getopt (argc, argv, "fdiqp:v")) != -1) {
1302 switch (c) {
1303 case 'd':
1304 isDaemon = 1;
1305 verbose = 0;
1306 break;
1308 case 'v':
1309 verbose = 1;
1310 break;
1312 case 'f':
1313 ftp = 1;
1314 break;
1316 case 'q':
1317 verbose = 0;
1318 break;
1320 case 'p':
1321 portnum = atoi (optarg);
1322 break;
1324 case 'i':
1325 inetd_started = 1;
1326 break;
1328 case 'r':
1329 r_auth = 1;
1330 break;
1332 default:
1333 fprintf (stderr,
1334 "Usage is: mcserv [options] [-p portnum]\n\n"
1335 "options are:\n" "-d become a daemon (sets -q)\n"
1336 "-q quiet mode\n"
1337 /* "-r use rhost based authentication\n" */
1338 #ifndef HAVE_PAM
1339 "-f force ftp authentication\n"
1340 #endif
1341 "-v verbose mode\n"
1342 "-p to specify a port number to listen\n");
1343 exit (0);
1348 if (isDaemon && fork ())
1349 exit (0);
1351 if (portnum == 0)
1352 portnum = get_port_number ();
1354 if (portnum != -1) {
1355 register_port (portnum, 0);
1356 if (verbose)
1357 printf ("Using port %d\n", portnum);
1358 if ((result = get_client (portnum)))
1359 perror (result);
1360 #ifdef HAVE_PMAP_SET
1361 if (!isDaemon)
1362 pmap_unset (RPC_PROGNUM, RPC_PROGVER);
1363 #endif
1365 exit (return_code);
1368 /* FIXME: This function should not be used in mcserv */
1369 void
1370 vfs_die (const char *m)
1372 fputs (m, stderr);
1373 exit (1);