Files
ncpfs/sutil/ncpmount.c
ncpfs archive import c6c6fbe4ca Import ncpfs 2.1.1
2026-04-28 20:39:59 +02:00

698 lines
16 KiB
C

/*
* ncpmount.c
*
* Copyright (C) 1995, 1997 by Volker Lendecke
*
* 1/20/96 - Steven N. Hirsch (hirsch@emba.uvm.edu)
*
* If the ncpfs support is not loaded and we are using kerneld to
* autoload modules, then we don't want to do it here. I added
* a conditional which leaves out the test and load code.
*
* Even if we _do_ want ncpmount to load the module, passing a
* fully-qualified pathname to modprobe causes it to bypass a
* path search. This may lead to ncpfs.o not being found on
* some systems.
*/
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <pwd.h>
#include <grp.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/stat.h>
#include <sys/types.h>
extern pid_t waitpid(pid_t, int *, int);
#include <sys/errno.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <mntent.h>
#include <linux/ipx.h>
#include <sys/ioctl.h>
#include <utmp.h>
#include <syslog.h>
#include <linux/fs.h>
#include <linux/ncp.h>
#include <linux/ncp_fs.h>
#include <linux/ncp_mount.h>
#include "ncplib.h"
#include "com_err.h"
static char *progname;
static char mount_point[MAXPATHLEN + 1];
static void usage(void);
static void help(void);
static int process_connection(int wdog_fd, int msg_fd);
/* Check whether user is allowed to mount on the specified mount point */
static int mount_ok(struct stat *st)
{
if (!S_ISDIR(st->st_mode)) {
errno = ENOTDIR;
return -1;
}
if ((getuid() != 0)
&& ((getuid() != st->st_uid)
|| ((st->st_mode & S_IRWXU) != S_IRWXU))) {
errno = EPERM;
return -1;
}
return 0;
}
/*
* This function changes the processes name as shown by the system.
* Stolen from Marcin Dalecki's modald :-)
*/
static void inststr(char *dst[], int argc, char *src)
{
/* stolen from the source to perl 4.036 (assigning to $0) */
char *ptr, *ptr2;
int count;
ptr = dst[0] + strlen(dst[0]);
for (count = 1; count < argc; count++) {
if (dst[count] == ptr + 1)
ptr += strlen(++ptr);
}
if (environ[0] == ptr + 1) {
for (count = 0; environ[count]; count++)
if (environ[count] == ptr + 1)
ptr += strlen(++ptr);
}
count = 0;
for (ptr2 = dst[0]; ptr2 <= ptr; ptr2++) {
*ptr2 = '\0';
count++;
}
strncpy(dst[0], src, count);
for (count = 1; count < argc; count++) {
dst[count] = NULL;
}
}
int main(int argc, char *argv[])
{
struct ncp_mount_data data;
struct stat st;
char mount_name[256];
int fd, result;
int wdog_fd, msg_fd;
struct sockaddr_ipx addr;
struct sockaddr_ipx *server_addr;
int addrlen;
int upcase_password;
long err;
int um;
unsigned int flags;
struct mntent ment;
FILE *mtab;
char *server = NULL;
char *user = NULL;
char *password = NULL;
struct ncp_conn_spec *spec;
uid_t conn_uid = getuid();
struct ncp_conn *conn;
int opt;
progname = argv[0];
memzero(data);
memzero(spec);
if (geteuid() != 0) {
fprintf(stderr, "%s must be installed suid root\n", progname);
exit(1);
}
data.uid = getuid();
data.gid = getgid();
um = umask(0);
umask(um);
data.file_mode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~um;
data.dir_mode = 0;
data.flags |= NCP_MOUNT_SOFT;
data.time_out = 60;
data.retry_count = 5;
upcase_password = 1;
while ((opt = getopt(argc, argv, "CS:U:c:u:g:f:d:P:nh?vV:t:r:"))
!= EOF) {
switch (opt) {
case 'C':
upcase_password = 0;
break;
case 'S':
if (strlen(optarg) >= sizeof(spec->server)) {
fprintf(stderr, "Servername too long:%s\n",
optarg);
return 1;
}
server = optarg;
break;
case 'U':
if (strlen(optarg) >= sizeof(spec->user)) {
fprintf(stderr, "Username too long: %s\n",
optarg);
return 1;
}
user = optarg;
break;
case 'c':
if (isdigit(optarg[0])) {
conn_uid = atoi(optarg);
} else {
struct passwd *pwd = getpwnam(optarg);
if (pwd == NULL) {
fprintf(stderr, "Unknown user: %s\n",
optarg);
return 1;
}
data.uid = pwd->pw_uid;
}
break;
case 'u':
if (isdigit(optarg[0])) {
data.uid = atoi(optarg);
} else {
struct passwd *pwd = getpwnam(optarg);
if (pwd == NULL) {
fprintf(stderr, "Unknown user: %s\n",
optarg);
return 1;
}
data.uid = pwd->pw_uid;
}
break;
case 'g':
if (isdigit(optarg[0])) {
data.gid = atoi(optarg);
} else {
struct group *grp = getgrnam(optarg);
if (grp == NULL) {
fprintf(stderr, "Unknown group: %s\n",
optarg);
return 1;
}
data.gid = grp->gr_gid;
}
break;
case 'f':
data.file_mode = strtol(optarg, NULL, 8);
break;
case 'd':
data.dir_mode = strtol(optarg, NULL, 8);
break;
case 'P':
if (strlen(optarg) >= sizeof(spec->password)) {
printf("password too long\n");
exit(1);
}
password = optarg;
break;
case 'V':
if (strlen(optarg) >= sizeof(data.mounted_vol)) {
printf("Volume too long: %s\n", optarg);
exit(1);
}
strcpy(data.mounted_vol, optarg);
break;
case 'n':
password = "";
break;
case 't':
data.time_out = atoi(optarg);
break;
case 'r':
data.retry_count = atoi(optarg);
break;
case 'h':
case '?':
help();
exit(1);
case 'v':
fprintf(stderr, "ncpfs version %s\n", NCPFS_VERSION);
exit(1);
default:
usage();
return -1;
}
}
if ((spec = ncp_find_conn_spec(server, user, password,
1, data.uid, &err))
== NULL) {
com_err(progname, err, "in find_conn_spec");
exit(1);
}
if (upcase_password != 0) {
str_upper(spec->password);
}
if (optind != argc - 1) {
usage();
return -1;
}
realpath(argv[optind], mount_point);
if (stat(mount_point, &st) == -1) {
fprintf(stderr, "could not find mount point %s: %s\n",
mount_point, strerror(errno));
exit(1);
}
if (mount_ok(&st) != 0) {
fprintf(stderr, "cannot to mount on %s: %s\n",
mount_point, strerror(errno));
exit(1);
}
#if NCP_MOUNT_VERSION < 3
#error "Use Linux 2.1.29 with patch applied !!!"
#endif
data.version = NCP_MOUNT_VERSION;
data.mounted_uid = conn_uid;
if (data.dir_mode == 0) {
data.dir_mode = data.file_mode;
if ((data.dir_mode & S_IRUSR) != 0)
data.dir_mode |= S_IXUSR;
if ((data.dir_mode & S_IRGRP) != 0)
data.dir_mode |= S_IXGRP;
if ((data.dir_mode & S_IROTH) != 0)
data.dir_mode |= S_IXOTH;
}
if ((server_addr = ncp_find_fileserver(spec->server, &err)) == NULL) {
com_err("ncpmount", err, "when trying to find %s",
spec->server);
exit(1);
}
data.ncp_fd = socket(AF_IPX, SOCK_DGRAM, PF_IPX);
if (data.ncp_fd == -1) {
com_err("ncpmount", err, "opening ncp_socket");
exit(1);
}
wdog_fd = socket(AF_IPX, SOCK_DGRAM, PF_IPX);
if (wdog_fd == -1) {
fprintf(stderr, "could not open wdog socket: %s\n",
strerror(errno));
exit(1);
}
memzero(addr);
addr.sipx_type = NCP_PTYPE;
if (bind(data.ncp_fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
fprintf(stderr, "\nbind: %s\n",
strerror(errno));
fprintf(stderr,
"\nMaybe you want to use \n"
"ipx_configure --auto_interface=on --auto_primary=on\n"
"and try again after waiting a minute.\n\n");
exit(1);
}
addrlen = sizeof(addr);
if (getsockname(data.ncp_fd,
(struct sockaddr *) &addr, &addrlen) == -1) {
perror("getsockname ncp socket");
close(data.ncp_fd);
close(wdog_fd);
exit(1);
}
addr.sipx_port = htons(ntohs(addr.sipx_port) + 1);
if (bind(wdog_fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
fprintf(stderr, "bind(wdog_sock, ): %s\n",
strerror(errno));
exit(1);
}
msg_fd = socket(AF_IPX, SOCK_DGRAM, PF_IPX);
if (msg_fd == -1) {
fprintf(stderr, "could not open message socket: %s\n",
strerror(errno));
exit(1);
}
addr.sipx_port = htons(ntohs(addr.sipx_port) + 1);
if (bind(msg_fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
fprintf(stderr, "bind(message_sock, ): %s\n",
strerror(errno));
exit(1);
}
connect(data.ncp_fd,
(struct sockaddr *) server_addr, sizeof(*server_addr));
flags = MS_MGC_VAL;
strcpy(mount_name, spec->server);
strcat(mount_name, "/");
strcat(mount_name, spec->user);
data.wdog_pid = fork();
if (data.wdog_pid < 0) {
fprintf(stderr, "could not fork: %s\n", strerror(errno));
exit(1);
}
if (data.wdog_pid == 0) {
/* Child */
inststr(argv, argc, "ncpd");
process_connection(wdog_fd, msg_fd);
}
result = mount(mount_name, mount_point, "ncpfs", flags, (char *) &data);
if (result < 0) {
printf("mount failed\n");
exit(1);
}
if ((conn = ncp_open_mount(mount_point, &err)) == NULL) {
com_err("ncpmount", err, "attempt to open mount point");
umount(mount_point);
exit(1);
}
if ((err = ncp_login_user(conn, spec->user, spec->password)) != 0) {
struct nw_property p;
struct ncp_prop_login_control *l
= (struct ncp_prop_login_control *) &p;
if (conn->completion != NCP_GRACE_PERIOD) {
com_err("ncpmount", err, "in login");
fprintf(stderr, "Login denied\n");
ncp_close(conn);
umount(mount_point);
exit(1);
}
fprintf(stderr, "Your password has expired\n");
if ((err = ncp_read_property_value(conn, NCP_BINDERY_USER,
spec->user, 1,
"LOGIN_CONTROL", &p)) == 0) {
fprintf(stderr, "You have %d login attempts left\n",
l->GraceLogins);
}
}
if ((err = ioctl(conn->mount_fid, NCP_IOC_CONN_LOGGED_IN, NULL)) != 0) {
com_err("ncpmount", err, "in logged_indication");
ncp_close(conn);
umount(mount_point);
exit(1);
}
ncp_close(conn);
ment.mnt_fsname = mount_name;
ment.mnt_dir = mount_point;
ment.mnt_type = "ncpfs";
ment.mnt_opts = "rw";
ment.mnt_freq = 0;
ment.mnt_passno = 0;
if ((fd = open(MOUNTED "~", O_RDWR | O_CREAT | O_EXCL, 0600)) == -1) {
fprintf(stderr, "Can't get " MOUNTED "~ lock file");
exit(1);
}
close(fd);
if ((mtab = setmntent(MOUNTED, "a+")) == NULL) {
fprintf(stderr, "Can't open " MOUNTED);
exit(1);
}
if (addmntent(mtab, &ment) == 1) {
fprintf(stderr, "Can't write mount entry");
exit(1);
}
if (fchmod(fileno(mtab), 0644) == -1) {
fprintf(stderr, "Can't set perms on " MOUNTED);
exit(1);
}
endmntent(mtab);
if (unlink(MOUNTED "~") == -1) {
fprintf(stderr, "Can't remove " MOUNTED "~");
exit(1);
}
return 0;
}
static void usage(void)
{
printf("usage: %s [options] mount-point\n", progname);
printf("Try `%s -h' for more information\n", progname);
}
static void help(void)
{
printf("\n");
printf("usage: %s [options] mount-point\n", progname);
printf("\n"
"-S server Server name to be used\n"
"-U username Username sent to server\n"
"-V volume Volume to mount, for NFS re-export\n"
"-u uid uid the mounted files get\n"
"-g gid gid the mounted files get\n"
"-f mode permission the files get (octal notation)\n"
"-d mode permission the dirs get (octal notation)\n"
"-c uid uid to identify the connection to mount on\n"
" Only makes sense for root\n"
"-t time_out Waiting time (in 1/100s) to wait for\n"
" an answer from the server. Default: 60\n"
"-r retry_count Number of retry attempts. Default: 5\n"
"-C Don't convert password to uppercase\n"
"-P password Use this password\n"
"-n Do not use any password\n"
" If neither -P nor -n are given, you are\n"
" asked for a password.\n"
"-h print this help text\n"
"-v print ncpfs version number\n"
"\n");
}
/* The following routines have been taken from util-linux-2.5's write.c */
/*
* term_chk - check that a terminal exists, and get the message bit
* and the access time
*/
static int term_chk(char *tty, int *msgsokP, time_t * atimeP, int *showerror)
{
struct stat s;
char path[MAXPATHLEN];
(void) sprintf(path, "/dev/%s", tty);
if (stat(path, &s) < 0) {
if (showerror)
(void) fprintf(stderr,
"write: %s: %s\n", path, strerror(errno));
return (1);
}
*msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0; /* group write bit */
*atimeP = s.st_atime;
return (0);
}
/*
* search_utmp - search utmp for the "best" terminal to write to
*
* Ignores terminals with messages disabled, and of the rest, returns
* the one with the most recent access time. Returns as value the number
* of the user's terminals with messages enabled, or -1 if the user is
* not logged in at all.
*
* Special case for writing to yourself - ignore the terminal you're
* writing from, unless that's the only terminal with messages enabled.
*/
static int search_utmp(char *user, char *tty)
{
struct utmp u;
time_t bestatime, atime;
int ufd, nloggedttys, nttys, msgsok, user_is_me;
char atty[sizeof(u.ut_line) + 1];
if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) {
perror("utmp");
return -1;
}
nloggedttys = nttys = 0;
bestatime = 0;
user_is_me = 0;
while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) {
++nloggedttys;
(void) strncpy(atty, u.ut_line, sizeof(u.ut_line));
atty[sizeof(u.ut_line)] = '\0';
if (term_chk(atty, &msgsok, &atime, 0))
continue; /* bad term? skip */
if (!msgsok)
continue; /* skip ttys with msgs off */
if (u.ut_type != USER_PROCESS)
continue; /* it's not a valid entry */
++nttys;
if (atime > bestatime) {
bestatime = atime;
(void) strcpy(tty, atty);
}
}
(void) close(ufd);
if (nloggedttys == 0) {
(void) fprintf(stderr, "write: %s is not logged in\n", user);
return -1;
}
return 0;
}
static void msg_received(void)
{
struct ncp_conn *conn;
char message[256];
struct ncp_fs_info info;
struct passwd *pwd;
char tty[256];
char tty_path[256];
FILE *tty_file;
FILE *mtab;
struct mntent *mnt;
long err;
openlog("nwmsg", LOG_PID, LOG_LPR);
if ((conn = ncp_open_mount(mount_point, &err)) == NULL) {
return;
}
if (ncp_get_broadcast_message(conn, message) != 0) {
ncp_close(conn);
return;
}
if (strlen(message) == 0) {
syslog(LOG_DEBUG, "no message");
ncp_close(conn);
return;
}
syslog(LOG_DEBUG, "message: %s", message);
info.version = NCP_GET_FS_INFO_VERSION;
if (ioctl(conn->mount_fid, NCP_IOC_GET_FS_INFO, &info) < 0) {
ncp_close(conn);
return;
}
ncp_close(conn);
if ((pwd = getpwuid(info.mounted_uid)) == NULL) {
fprintf(stderr, "%s: user %d not known\n",
progname, info.mounted_uid);
return;
}
if ((mtab = fopen(MOUNTED, "r")) == NULL) {
fprintf(stderr, "%s: can't open %s\n",
progname, MOUNTED);
return;
}
while ((mnt = getmntent(mtab)) != NULL) {
if (strcmp(mnt->mnt_dir, mount_point) == 0) {
break;
}
}
if (mnt == NULL) {
syslog(LOG_DEBUG, "cannot find mtab entry\n");
}
if (search_utmp(pwd->pw_name, tty) != 0) {
return;
}
sprintf(tty_path, "/dev/%s", tty);
if ((tty_file = fopen(tty_path, "w")) == NULL) {
fprintf(stderr, "%s: cannot open %s: %s\n",
progname, tty_path, strerror(errno));
return;
}
fprintf(tty_file, "\r\n\007\007\007Message from NetWare Server: %s\r\n",
mnt->mnt_fsname);
fprintf(tty_file, "%s\r\n", message);
fclose(tty_file);
fclose(mtab);
return;
}
static void process_msg_packet(int msg_fd)
{
struct sockaddr_ipx sender;
int addrlen = sizeof(sender);
char buf[1024];
if (recvfrom(msg_fd, buf, sizeof(buf), MSG_DONTWAIT,
(struct sockaddr *) &sender, &addrlen) <= 0) {
return;
}
msg_received();
}
static void process_wdog_packet(int wdog_fd)
{
struct sockaddr_ipx sender;
int addrlen = sizeof(sender);
char buf[2];
if (recvfrom(wdog_fd, buf, sizeof(buf), MSG_DONTWAIT,
(struct sockaddr *) &sender, &addrlen) < sizeof(buf)) {
return;
}
if (buf[1] != '?') {
return;
}
buf[1] = 'Y';
sendto(wdog_fd, buf, 2, 0, (struct sockaddr *) &sender, addrlen);
}
static int process_connection(int wdog_fd, int msg_fd)
{
int i;
int result;
int max;
chdir("/");
setsid();
for (i = 0; i < NR_OPEN; i++) {
if ((i == wdog_fd) || (i == msg_fd)) {
continue;
}
close(i);
}
max = (wdog_fd > msg_fd ? wdog_fd : msg_fd) + 1;
while (1) {
fd_set rd;
FD_ZERO(&rd);
FD_SET(wdog_fd, &rd);
FD_SET(msg_fd, &rd);
if ((result = select(max, &rd, NULL, NULL, NULL)) == -1) {
exit(0);
}
if (FD_ISSET(wdog_fd, &rd)) {
process_wdog_packet(wdog_fd);
}
if (FD_ISSET(msg_fd, &rd)) {
process_msg_packet(msg_fd);
}
}
}