Blame mount.cifs.c

Packit 5f9837
/*
Packit 5f9837
 * Mount helper utility for Linux CIFS VFS (virtual filesystem) client
Packit 5f9837
 * Copyright (C) 2003,2010 Steve French  (sfrench@us.ibm.com)
Packit 5f9837
 * Copyright (C) 2008 Jeremy Allison (jra@samba.org)
Packit 5f9837
 * Copyright (C) 2010 Jeff Layton (jlayton@samba.org)
Packit 5f9837
 *
Packit 5f9837
 * This program is free software; you can redistribute it and/or modify
Packit 5f9837
 * it under the terms of the GNU General Public License as published by
Packit 5f9837
 * the Free Software Foundation; either version 3 of the License, or
Packit 5f9837
 * (at your option) any later version.
Packit 5f9837
 *
Packit 5f9837
 * This program is distributed in the hope that it will be useful,
Packit 5f9837
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 5f9837
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 5f9837
 * GNU General Public License for more details.
Packit 5f9837
 *
Packit 5f9837
 * You should have received a copy of the GNU General Public License
Packit 5f9837
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 5f9837
 */
Packit 5f9837
Packit 5f9837
#ifdef HAVE_CONFIG_H
Packit 5f9837
#include "config.h"
Packit 5f9837
#endif /* HAVE_CONFIG_H */
Packit 5f9837
Packit 5f9837
#include <stdlib.h>
Packit 5f9837
#include <stdio.h>
Packit 5f9837
#include <unistd.h>
Packit 5f9837
#include <pwd.h>
Packit 5f9837
#include <grp.h>
Packit 5f9837
#include <ctype.h>
Packit 5f9837
#include <sys/types.h>
Packit 5f9837
#include <sys/mount.h>
Packit 5f9837
#include <sys/stat.h>
Packit 5f9837
#include <sys/utsname.h>
Packit 5f9837
#include <sys/socket.h>
Packit 5f9837
#include <arpa/inet.h>
Packit 5f9837
#include <getopt.h>
Packit 5f9837
#include <errno.h>
Packit 5f9837
#include <netdb.h>
Packit 5f9837
#include <string.h>
Packit 5f9837
#include <mntent.h>
Packit 5f9837
#include <fcntl.h>
Packit 5f9837
#include <limits.h>
Packit 5f9837
#include <paths.h>
Packit 5f9837
#include <libgen.h>
Packit 5f9837
#include <sys/mman.h>
Packit 5f9837
#include <sys/wait.h>
Packit 5f9837
#ifdef HAVE_SYS_FSUID_H
Packit 5f9837
#include <sys/fsuid.h>
Packit 5f9837
#endif /* HAVE_SYS_FSUID_H */
Packit 5f9837
#ifdef HAVE_LIBCAP_NG
Packit 5f9837
#include <cap-ng.h>
Packit 5f9837
#else /* HAVE_LIBCAP_NG */
Packit 5f9837
#ifdef HAVE_PRCTL
Packit 5f9837
#include <sys/prctl.h>
Packit 5f9837
#endif /* HAVE_PRCTL */
Packit 5f9837
#ifdef HAVE_LIBCAP
Packit 5f9837
#include <sys/capability.h>
Packit 5f9837
#endif /* HAVE_LIBCAP */
Packit 5f9837
#endif /* HAVE_LIBCAP_NG */
Packit 5f9837
#include "mount.h"
Packit 5f9837
#include "util.h"
Packit 5f9837
#include "resolve_host.h"
Packit 5f9837
Packit 5f9837
#ifndef MS_MOVE 
Packit 5f9837
#define MS_MOVE 8192 
Packit 5f9837
#endif 
Packit 5f9837
Packit 5f9837
#ifndef MS_BIND
Packit 5f9837
#define MS_BIND 4096
Packit 5f9837
#endif
Packit 5f9837
Packit 5f9837
/* private flags - clear these before passing to kernel */
Packit 5f9837
#define MS_USERS	0x40000000
Packit 5f9837
#define MS_USER		0x80000000
Packit 5f9837
Packit 5f9837
#define MAX_UNC_LEN 1024
Packit 5f9837
Packit 5f9837
/* I believe that the kernel limits options data to a page */
Packit 5f9837
#define MAX_OPTIONS_LEN	4096
Packit 5f9837
Packit 5f9837
/* max length of mtab options */
Packit 5f9837
#define MTAB_OPTIONS_LEN 220
Packit 5f9837
Packit 5f9837
/*
Packit 5f9837
 * Max share name, username, password and domain sizes match the kernel's
Packit 5f9837
 * allowances for these string sizes which in turn match Microsoft's
Packit 5f9837
 * documentation.
Packit 5f9837
 */
Packit 5f9837
Packit 5f9837
/* Max length of the share name portion of a UNC. Share names over 80
Packit 5f9837
 * characters cannot be accessed via commandline in Windows 2000/XP. */
Packit 5f9837
#define MAX_SHARE_LEN 256
Packit 5f9837
Packit 5f9837
/* Max user name length. */
Packit 5f9837
#define MAX_USERNAME_SIZE 256
Packit 5f9837
Packit 5f9837
/* Max domain size. */
Packit 5f9837
#define MAX_DOMAIN_SIZE 256
Packit 5f9837
Packit 5f9837
/* Max password size. */
Packit 5f9837
#define MOUNT_PASSWD_SIZE 512
Packit 5f9837
Packit 5f9837
/*
Packit 5f9837
 * mount.cifs has been the subject of many "security" bugs that have arisen
Packit 5f9837
 * because of users and distributions installing it as a setuid root program
Packit 5f9837
 * before it had been audited for security holes. The default behavior is
Packit 5f9837
 * now to allow mount.cifs to be run as a setuid root program. Some admins
Packit 5f9837
 * may want to disable this fully, so this switch remains in place.
Packit 5f9837
 */
Packit 5f9837
#define CIFS_DISABLE_SETUID_CAPABILITY 0
Packit 5f9837
Packit 5f9837
/*
Packit 5f9837
 * When an unprivileged user runs a setuid mount.cifs, we set certain mount
Packit 5f9837
 * flags by default. These defaults can be changed here.
Packit 5f9837
 */
Packit 5f9837
#define CIFS_SETUID_FLAGS (MS_NOSUID|MS_NODEV)
Packit 5f9837
Packit 5f9837
/*
Packit 5f9837
 * Values for parsing a credentials file.
Packit 5f9837
 */
Packit 5f9837
#define CRED_UNPARSEABLE 0
Packit 5f9837
#define CRED_USER        1
Packit 5f9837
#define CRED_PASS        2
Packit 5f9837
#define CRED_DOM         4
Packit 5f9837
Packit 5f9837
/*
Packit 5f9837
 * Values for parsing command line options.
Packit 5f9837
 */
Packit 5f9837
#define OPT_ERROR       -1
Packit 5f9837
#define OPT_IGNORE      0
Packit 5f9837
#define OPT_USERS       1
Packit 5f9837
#define OPT_USER        2
Packit 5f9837
#define OPT_USER_XATTR  3
Packit 5f9837
#define OPT_PASS        4
Packit 5f9837
#define OPT_SEC         5
Packit 5f9837
#define OPT_IP          6
Packit 5f9837
#define OPT_UNC         7
Packit 5f9837
#define OPT_CRED        8
Packit 5f9837
#define OPT_UID         9
Packit 5f9837
#define OPT_GID        10
Packit 5f9837
#define OPT_FMASK      11
Packit 5f9837
#define OPT_FILE_MODE  12
Packit 5f9837
#define OPT_DMASK      13
Packit 5f9837
#define OPT_DIR_MODE   14
Packit 5f9837
#define OPT_DOM        15
Packit 5f9837
#define OPT_NO_SUID    16
Packit 5f9837
#define OPT_SUID       17
Packit 5f9837
#define OPT_NO_DEV     18
Packit 5f9837
#define OPT_DEV        19
Packit 5f9837
#define OPT_NO_LOCK    20
Packit 5f9837
#define OPT_NO_EXEC    21
Packit 5f9837
#define OPT_EXEC       22
Packit 5f9837
#define OPT_GUEST      23
Packit 5f9837
#define OPT_RO         24
Packit 5f9837
#define OPT_RW         25
Packit 5f9837
#define OPT_REMOUNT    26
Packit 5f9837
#define OPT_MAND       27
Packit 5f9837
#define OPT_NOMAND     28
Packit 5f9837
#define OPT_CRUID      29
Packit 5f9837
#define OPT_BKUPUID    30
Packit 5f9837
#define OPT_BKUPGID    31
Packit 5f9837
#define OPT_NOFAIL     32
Packit 5f9837
Packit 5f9837
#define MNT_TMP_FILE "/.mtab.cifs.XXXXXX"
Packit 5f9837
Packit 5f9837
/* struct for holding parsed mount info for use by privleged process */
Packit 5f9837
struct parsed_mount_info {
Packit 5f9837
	unsigned long flags;
Packit 5f9837
	char host[NI_MAXHOST + 1];
Packit 5f9837
	char share[MAX_SHARE_LEN + 1];
Packit 5f9837
	char prefix[PATH_MAX + 1];
Packit 5f9837
	char options[MAX_OPTIONS_LEN];
Packit 5f9837
	char domain[MAX_DOMAIN_SIZE + 1];
Packit 5f9837
	char username[MAX_USERNAME_SIZE + 1];
Packit 5f9837
	char password[MOUNT_PASSWD_SIZE + 1];
Packit 5f9837
	char addrlist[MAX_ADDR_LIST_LEN];
Packit 5f9837
	unsigned int got_user:1;
Packit 5f9837
	unsigned int got_password:1;
Packit 5f9837
	unsigned int fakemnt:1;
Packit 5f9837
	unsigned int nomtab:1;
Packit 5f9837
	unsigned int verboseflag:1;
Packit 5f9837
	unsigned int nofail:1;
Packit 5f9837
	unsigned int got_domain:1;
Packit 5f9837
};
Packit 5f9837
Packit 5f9837
static const char *thisprogram;
Packit 5f9837
static const char *cifs_fstype = "cifs";
Packit 5f9837
Packit 5f9837
static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info);
Packit 5f9837
Packit 5f9837
static int check_setuid(void)
Packit 5f9837
{
Packit 5f9837
	if (geteuid()) {
Packit 5f9837
		fprintf(stderr, "This program is not installed setuid root - "
Packit 5f9837
			" \"user\" CIFS mounts not supported.\n");
Packit 5f9837
		return EX_USAGE;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
#if CIFS_DISABLE_SETUID_CAPABILITY
Packit 5f9837
	if (getuid() && !geteuid()) {
Packit 5f9837
		printf("This mount.cifs program has been built with the "
Packit 5f9837
		       "ability to run as a setuid root program disabled.\n");
Packit 5f9837
		return EX_USAGE;
Packit 5f9837
	}
Packit 5f9837
#endif /* CIFS_DISABLE_SETUID_CAPABILITY */
Packit 5f9837
Packit 5f9837
	return 0;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
static int
Packit 5f9837
check_fstab(const char *progname, const char *mountpoint, const char *devname,
Packit 5f9837
	    char **options)
Packit 5f9837
{
Packit 5f9837
	FILE *fstab;
Packit 5f9837
	struct mntent *mnt;
Packit 5f9837
Packit 5f9837
	/* make sure this mount is listed in /etc/fstab */
Packit 5f9837
	fstab = setmntent(_PATH_MNTTAB, "r");
Packit 5f9837
	if (!fstab) {
Packit 5f9837
		fprintf(stderr, "Couldn't open %s for reading!\n", _PATH_MNTTAB);
Packit 5f9837
		return EX_FILEIO;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	while ((mnt = getmntent(fstab))) {
Packit 5f9837
		if (!strcmp(mountpoint, mnt->mnt_dir))
Packit 5f9837
			break;
Packit 5f9837
	}
Packit 5f9837
	endmntent(fstab);
Packit 5f9837
Packit 5f9837
	if (mnt == NULL || strcmp(mnt->mnt_fsname, devname)) {
Packit 5f9837
		fprintf(stderr, "%s: permission denied: no match for "
Packit 5f9837
			"%s found in %s\n", progname, mountpoint, _PATH_MNTTAB);
Packit 5f9837
		return EX_USAGE;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	/*
Packit 5f9837
	 * 'mount' munges the options from fstab before passing them
Packit 5f9837
	 * to us. It is non-trivial to test that we have the correct
Packit 5f9837
	 * set of options. We don't want to trust what the user
Packit 5f9837
	 * gave us, so just take whatever is in /etc/fstab.
Packit 5f9837
	 */
Packit 5f9837
	free(*options);
Packit 5f9837
	*options = strdup(mnt->mnt_opts);
Packit 5f9837
	return 0;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
/* BB finish BB
Packit 5f9837
Packit 5f9837
	cifs_umount
Packit 5f9837
	open nofollow - avoid symlink exposure? 
Packit 5f9837
	get owner of dir see if matches self or if root
Packit 5f9837
	call system(umount argv) etc.
Packit 5f9837
Packit 5f9837
BB end finish BB */
Packit 5f9837
Packit 5f9837
static int mount_usage(FILE * stream)
Packit 5f9837
{
Packit 5f9837
	fprintf(stream, "\nUsage:  %s <remotetarget> <dir> -o <options>\n",
Packit 5f9837
		thisprogram);
Packit 5f9837
	fprintf(stream, "\nMount the remote target, specified as a UNC name,");
Packit 5f9837
	fprintf(stream, " to a local directory.\n\nOptions:\n");
Packit 5f9837
	fprintf(stream, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
Packit 5f9837
	fprintf(stream, "\nLess commonly used options:");
Packit 5f9837
	fprintf(stream,
Packit 5f9837
		"\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
Packit 5f9837
	fprintf(stream,
Packit 5f9837
		"\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
Packit 5f9837
	fprintf(stream,
Packit 5f9837
		"\n\tmapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
Packit 5f9837
	fprintf(stream,
Packit 5f9837
		"\n\tdirectio,nounix,cifsacl,sec=<authentication mechanism>,sign,seal,fsc");
Packit 5f9837
	fprintf(stream,
Packit 5f9837
		"\n\nOptions not needed for servers supporting CIFS Unix extensions");
Packit 5f9837
	fprintf(stream,
Packit 5f9837
		"\n\t(e.g. unneeded for mounts to most Samba versions):");
Packit 5f9837
	fprintf(stream,
Packit 5f9837
		"\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu");
Packit 5f9837
	fprintf(stream, "\n\nRarely used options:");
Packit 5f9837
	fprintf(stream,
Packit 5f9837
		"\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
Packit 5f9837
	fprintf(stream,
Packit 5f9837
		"\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
Packit 5f9837
	fprintf(stream,
Packit 5f9837
		"\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl");
Packit 5f9837
	fprintf(stream,
Packit 5f9837
		"\n\nOptions are described in more detail in the manual page");
Packit 5f9837
	fprintf(stream, "\n\tman 8 mount.cifs\n");
Packit 5f9837
	fprintf(stream, "\nTo display the version number of the mount helper:");
Packit 5f9837
	fprintf(stream, "\n\t%s -V\n", thisprogram);
Packit 5f9837
Packit 5f9837
	if (stream == stderr)
Packit 5f9837
		return EX_USAGE;
Packit 5f9837
	return 0;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
/*
Packit 5f9837
 * CIFS has to "escape" commas in the password field so that they don't
Packit 5f9837
 * end up getting confused for option delimiters. Copy password into pw
Packit 5f9837
 * field, turning any commas into double commas.
Packit 5f9837
 */
Packit 5f9837
static int set_password(struct parsed_mount_info *parsed_info, const char *src)
Packit 5f9837
{
Packit 5f9837
	char *dst = parsed_info->password;
Packit 5f9837
	unsigned int i = 0, j = 0;
Packit 5f9837
Packit 5f9837
	while (src[i]) {
Packit 5f9837
		if (src[i] == ',')
Packit 5f9837
			dst[j++] = ',';
Packit 5f9837
		dst[j++] = src[i++];
Packit 5f9837
		if (j > sizeof(parsed_info->password)) {
Packit 5f9837
			fprintf(stderr, "Converted password too long!\n");
Packit 5f9837
			return EX_USAGE;
Packit 5f9837
		}
Packit 5f9837
	}
Packit 5f9837
	dst[j] = '\0';
Packit 5f9837
	parsed_info->got_password = 1;
Packit 5f9837
	return 0;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
#ifdef HAVE_LIBCAP_NG
Packit 5f9837
static int
Packit 5f9837
drop_capabilities(int parent)
Packit 5f9837
{
Packit 5f9837
	capng_setpid(getpid());
Packit 5f9837
	capng_clear(CAPNG_SELECT_BOTH);
Packit 5f9837
	if (parent) {
Packit 5f9837
		if (capng_updatev(CAPNG_ADD, CAPNG_PERMITTED, CAP_DAC_READ_SEARCH, CAP_DAC_OVERRIDE, -1)) {
Packit 5f9837
			fprintf(stderr, "Unable to update capability set.\n");
Packit 5f9837
			return EX_SYSERR;
Packit 5f9837
		}
Packit 5f9837
		if (capng_update(CAPNG_ADD, CAPNG_PERMITTED|CAPNG_EFFECTIVE, CAP_SYS_ADMIN)) {
Packit 5f9837
			fprintf(stderr, "Unable to update capability set.\n");
Packit 5f9837
			return EX_SYSERR;
Packit 5f9837
		}
Packit 5f9837
	} else {
Packit 5f9837
		if (capng_update(CAPNG_ADD, CAPNG_PERMITTED, CAP_DAC_READ_SEARCH)) {
Packit 5f9837
			fprintf(stderr, "Unable to update capability set.\n");
Packit 5f9837
			return EX_SYSERR;
Packit 5f9837
		}
Packit 5f9837
	}
Packit 5f9837
	if (capng_apply(CAPNG_SELECT_BOTH)) {
Packit 5f9837
		fprintf(stderr, "Unable to apply new capability set.\n");
Packit 5f9837
		return EX_SYSERR;
Packit 5f9837
	}
Packit 5f9837
	return 0;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
static int
Packit 5f9837
toggle_dac_capability(int writable, int enable)
Packit 5f9837
{
Packit 5f9837
	unsigned int capability = writable ? CAP_DAC_OVERRIDE : CAP_DAC_READ_SEARCH;
Packit 5f9837
Packit 5f9837
	if (capng_update(enable ? CAPNG_ADD : CAPNG_DROP, CAPNG_EFFECTIVE, capability)) {
Packit 5f9837
		fprintf(stderr, "Unable to update capability set.\n");
Packit 5f9837
		return EX_SYSERR;
Packit 5f9837
	}
Packit 5f9837
	if (capng_apply(CAPNG_SELECT_CAPS)) {
Packit 5f9837
		fprintf(stderr, "Unable to apply new capability set.\n");
Packit 5f9837
		return EX_SYSERR;
Packit 5f9837
	}
Packit 5f9837
	return 0;
Packit 5f9837
}
Packit 5f9837
#else /* HAVE_LIBCAP_NG */
Packit 5f9837
#ifdef HAVE_LIBCAP
Packit 5f9837
#ifdef HAVE_PRCTL
Packit 5f9837
static int
Packit 5f9837
prune_bounding_set(void)
Packit 5f9837
{
Packit 5f9837
	int i, rc = 0;
Packit 5f9837
	static int bounding_set_cleared;
Packit 5f9837
Packit 5f9837
	if (bounding_set_cleared)
Packit 5f9837
		return 0;
Packit 5f9837
Packit 5f9837
	for (i = 0; i <= CAP_LAST_CAP && rc == 0; ++i)
Packit 5f9837
		rc = prctl(PR_CAPBSET_DROP, i);
Packit 5f9837
Packit 5f9837
	if (rc != 0) {
Packit 5f9837
		fprintf(stderr, "Unable to clear capability bounding set: %d\n", rc);
Packit 5f9837
		return EX_SYSERR;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	++bounding_set_cleared;
Packit 5f9837
	return 0;
Packit 5f9837
}
Packit 5f9837
#else /* HAVE_PRCTL */
Packit 5f9837
static int
Packit 5f9837
prune_bounding_set(void)
Packit 5f9837
{
Packit 5f9837
	return 0;
Packit 5f9837
}
Packit 5f9837
#endif /* HAVE_PRCTL */
Packit 5f9837
static int
Packit 5f9837
drop_capabilities(int parent)
Packit 5f9837
{
Packit 5f9837
	int rc, ncaps;
Packit 5f9837
	cap_t caps;
Packit 5f9837
	cap_value_t cap_list[3];
Packit 5f9837
Packit 5f9837
	rc = prune_bounding_set();
Packit 5f9837
	if (rc)
Packit 5f9837
		return rc;
Packit 5f9837
Packit 5f9837
	caps = cap_get_proc();
Packit 5f9837
	if (caps == NULL) {
Packit 5f9837
		fprintf(stderr, "Unable to get current capability set: %s\n",
Packit 5f9837
			strerror(errno));
Packit 5f9837
		return EX_SYSERR;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	if (cap_clear(caps) == -1) {
Packit 5f9837
		fprintf(stderr, "Unable to clear capability set: %s\n",
Packit 5f9837
			strerror(errno));
Packit 5f9837
		rc = EX_SYSERR;
Packit 5f9837
		goto free_caps;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	if (parent || getuid() == 0) {
Packit 5f9837
		ncaps = 1;
Packit 5f9837
		cap_list[0] = CAP_DAC_READ_SEARCH;
Packit 5f9837
		if (parent) {
Packit 5f9837
			cap_list[1] = CAP_DAC_OVERRIDE;
Packit 5f9837
			cap_list[2] = CAP_SYS_ADMIN;
Packit 5f9837
			ncaps += 2;
Packit 5f9837
		}
Packit 5f9837
		if (cap_set_flag(caps, CAP_PERMITTED, ncaps, cap_list, CAP_SET) == -1) {
Packit 5f9837
			fprintf(stderr, "Unable to set permitted capabilities: %s\n",
Packit 5f9837
				strerror(errno));
Packit 5f9837
			rc = EX_SYSERR;
Packit 5f9837
			goto free_caps;
Packit 5f9837
		}
Packit 5f9837
		if (parent) {
Packit 5f9837
			cap_list[0] = CAP_SYS_ADMIN;
Packit 5f9837
			if (cap_set_flag(caps, CAP_EFFECTIVE, 1, cap_list, CAP_SET) == -1) {
Packit 5f9837
				fprintf(stderr, "Unable to set effective capabilities: %s\n",
Packit 5f9837
					strerror(errno));
Packit 5f9837
				rc = EX_SYSERR;
Packit 5f9837
				goto free_caps;
Packit 5f9837
			}
Packit 5f9837
		}
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	if (cap_set_proc(caps) != 0) {
Packit 5f9837
		fprintf(stderr, "Unable to set current process capabilities: %s\n",
Packit 5f9837
			strerror(errno));
Packit 5f9837
		rc = EX_SYSERR;
Packit 5f9837
	}
Packit 5f9837
free_caps:
Packit 5f9837
	cap_free(caps);
Packit 5f9837
	return rc;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
static int
Packit 5f9837
toggle_dac_capability(int writable, int enable)
Packit 5f9837
{
Packit 5f9837
	int rc = 0;
Packit 5f9837
	cap_t caps;
Packit 5f9837
	cap_value_t capability = writable ? CAP_DAC_OVERRIDE : CAP_DAC_READ_SEARCH;
Packit 5f9837
Packit 5f9837
	caps = cap_get_proc();
Packit 5f9837
	if (caps == NULL) {
Packit 5f9837
		fprintf(stderr, "Unable to get current capability set: %s\n",
Packit 5f9837
			strerror(errno));
Packit 5f9837
		return EX_SYSERR;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &capability,
Packit 5f9837
			 enable ? CAP_SET : CAP_CLEAR) == -1) {
Packit 5f9837
		fprintf(stderr, "Unable to %s effective capabilities: %s\n",
Packit 5f9837
			enable ? "set" : "clear", strerror(errno));
Packit 5f9837
		rc = EX_SYSERR;
Packit 5f9837
		goto free_caps;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	if (cap_set_proc(caps) != 0) {
Packit 5f9837
		fprintf(stderr, "Unable to set current process capabilities: %s\n",
Packit 5f9837
			strerror(errno));
Packit 5f9837
		rc = EX_SYSERR;
Packit 5f9837
	}
Packit 5f9837
free_caps:
Packit 5f9837
	cap_free(caps);
Packit 5f9837
	return rc;
Packit 5f9837
}
Packit 5f9837
#else /* HAVE_LIBCAP */
Packit 5f9837
static int
Packit 5f9837
drop_capabilities(int parent __attribute((unused)))
Packit 5f9837
{
Packit 5f9837
	return 0;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
static int
Packit 5f9837
toggle_dac_capability(int writable __attribute((unused)), int enable __attribute((unused)))
Packit 5f9837
{
Packit 5f9837
	return 0;
Packit 5f9837
}
Packit 5f9837
#endif /* HAVE_LIBCAP */
Packit 5f9837
#endif /* HAVE_LIBCAP_NG */
Packit 5f9837
Packit 5f9837
static void null_terminate_endl(char *source)
Packit 5f9837
{
Packit 5f9837
	char *newline = strchr(source, '\n');
Packit 5f9837
	if (newline)
Packit 5f9837
		*newline = '\0';
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
/*
Packit 5f9837
 * Parse a line from the credentials file.  Changes target to first
Packit 5f9837
 * character after '=' on 'line' and returns the value type of the line
Packit 5f9837
 * Returns CRED_UNPARSEABLE on failure or if either parameter is NULL.
Packit 5f9837
 */
Packit 5f9837
static int parse_cred_line(char *line, char **target)
Packit 5f9837
{
Packit 5f9837
	if (line == NULL || target == NULL)
Packit 5f9837
		goto parsing_err;
Packit 5f9837
Packit 5f9837
	/* position target at first char of value */
Packit 5f9837
	*target = strchr(line, '=');
Packit 5f9837
	if (!*target)
Packit 5f9837
		goto parsing_err;
Packit 5f9837
	*target += 1;
Packit 5f9837
Packit 5f9837
	/* tell the caller which value target points to */
Packit 5f9837
	if (strncasecmp("user", line, 4) == 0)
Packit 5f9837
		return CRED_USER;
Packit 5f9837
	if (strncasecmp("pass", line, 4) == 0)
Packit 5f9837
		return CRED_PASS;
Packit 5f9837
	if (strncasecmp("dom", line, 3) == 0)
Packit 5f9837
		return CRED_DOM;
Packit 5f9837
Packit 5f9837
parsing_err:
Packit 5f9837
	return CRED_UNPARSEABLE;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
static int open_cred_file(char *file_name,
Packit 5f9837
			struct parsed_mount_info *parsed_info)
Packit 5f9837
{
Packit 5f9837
	char *line_buf = NULL;
Packit 5f9837
	char *temp_val = NULL;
Packit 5f9837
	FILE *fs = NULL;
Packit 5f9837
	int i;
Packit 5f9837
	const int line_buf_size = 4096;
Packit 5f9837
	const int min_non_white = 10;
Packit 5f9837
Packit 5f9837
	i = toggle_dac_capability(0, 1);
Packit 5f9837
	if (i)
Packit 5f9837
		goto return_i;
Packit 5f9837
Packit 5f9837
	i = access(file_name, R_OK);
Packit 5f9837
	if (i) {
Packit 5f9837
		toggle_dac_capability(0, 0);
Packit 5f9837
		i = errno;
Packit 5f9837
		goto return_i;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	fs = fopen(file_name, "r");
Packit 5f9837
	if (fs == NULL) {
Packit 5f9837
		toggle_dac_capability(0, 0);
Packit 5f9837
		i = errno;
Packit 5f9837
		goto return_i;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	i = toggle_dac_capability(0, 0);
Packit 5f9837
	if (i)
Packit 5f9837
		goto return_i;
Packit 5f9837
Packit 5f9837
	line_buf = (char *)malloc(line_buf_size);
Packit 5f9837
	if (line_buf == NULL) {
Packit 5f9837
		i = EX_SYSERR;
Packit 5f9837
		goto return_i;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	/* parse line from credentials file */
Packit 5f9837
	while (fgets(line_buf, line_buf_size, fs)) {
Packit 5f9837
		/* eat leading white space */
Packit 5f9837
		for (i = 0; i < line_buf_size - min_non_white + 1; i++) {
Packit 5f9837
			if ((line_buf[i] != ' ') && (line_buf[i] != '\t'))
Packit 5f9837
				break;
Packit 5f9837
		}
Packit 5f9837
		null_terminate_endl(line_buf);
Packit 5f9837
Packit 5f9837
		/* parse next token */
Packit 5f9837
		switch (parse_cred_line(line_buf + i, &temp_val)) {
Packit 5f9837
		case CRED_USER:
Packit 5f9837
			strlcpy(parsed_info->username, temp_val,
Packit 5f9837
				sizeof(parsed_info->username));
Packit 5f9837
			parsed_info->got_user = 1;
Packit 5f9837
			break;
Packit 5f9837
		case CRED_PASS:
Packit 5f9837
			i = set_password(parsed_info, temp_val);
Packit 5f9837
			if (i)
Packit 5f9837
				goto return_i;
Packit 5f9837
			break;
Packit 5f9837
		case CRED_DOM:
Packit 5f9837
			if (parsed_info->verboseflag)
Packit 5f9837
				fprintf(stderr, "domain=%s\n",
Packit 5f9837
					temp_val);
Packit 5f9837
			strlcpy(parsed_info->domain, temp_val,
Packit 5f9837
				sizeof(parsed_info->domain));
Packit 5f9837
			break;
Packit 5f9837
		case CRED_UNPARSEABLE:
Packit 5f9837
			if (parsed_info->verboseflag)
Packit 5f9837
				fprintf(stderr, "Credential formatted "
Packit 5f9837
					"incorrectly: %s\n",
Packit 5f9837
					temp_val ? temp_val : "(null)");
Packit 5f9837
			break;
Packit 5f9837
		}
Packit 5f9837
	}
Packit 5f9837
	i = 0;
Packit 5f9837
return_i:
Packit 5f9837
	if (fs != NULL)
Packit 5f9837
		fclose(fs);
Packit 5f9837
Packit 5f9837
	/* make sure passwords are scrubbed from memory */
Packit 5f9837
	if (line_buf != NULL)
Packit 5f9837
		memset(line_buf, 0, line_buf_size);
Packit 5f9837
	free(line_buf);
Packit 5f9837
	return i;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
static int
Packit 5f9837
get_password_from_file(int file_descript, char *filename,
Packit 5f9837
		       struct parsed_mount_info *parsed_info)
Packit 5f9837
{
Packit 5f9837
	int rc = 0;
Packit 5f9837
	char buf[sizeof(parsed_info->password) + 1];
Packit 5f9837
Packit 5f9837
	if (filename != NULL) {
Packit 5f9837
		rc = toggle_dac_capability(0, 1);
Packit 5f9837
		if (rc)
Packit 5f9837
			return rc;
Packit 5f9837
Packit 5f9837
		rc = access(filename, R_OK);
Packit 5f9837
		if (rc) {
Packit 5f9837
			fprintf(stderr,
Packit 5f9837
				"mount.cifs failed: access check of %s failed: %s\n",
Packit 5f9837
				filename, strerror(errno));
Packit 5f9837
			toggle_dac_capability(0, 0);
Packit 5f9837
			return EX_SYSERR;
Packit 5f9837
		}
Packit 5f9837
Packit 5f9837
		file_descript = open(filename, O_RDONLY);
Packit 5f9837
		if (file_descript < 0) {
Packit 5f9837
			fprintf(stderr,
Packit 5f9837
				"mount.cifs failed. %s attempting to open password file %s\n",
Packit 5f9837
				strerror(errno), filename);
Packit 5f9837
			toggle_dac_capability(0, 0);
Packit 5f9837
			return EX_SYSERR;
Packit 5f9837
		}
Packit 5f9837
Packit 5f9837
		rc = toggle_dac_capability(0, 0);
Packit 5f9837
		if (rc) {
Packit 5f9837
			rc = EX_SYSERR;
Packit 5f9837
			goto get_pw_exit;
Packit 5f9837
		}
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	memset(buf, 0, sizeof(buf));
Packit 5f9837
	rc = read(file_descript, buf, sizeof(buf) - 1);
Packit 5f9837
	if (rc < 0) {
Packit 5f9837
		fprintf(stderr,
Packit 5f9837
			"mount.cifs failed. Error %s reading password file\n",
Packit 5f9837
			strerror(errno));
Packit 5f9837
		rc = EX_SYSERR;
Packit 5f9837
		goto get_pw_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	rc = set_password(parsed_info, buf);
Packit 5f9837
Packit 5f9837
get_pw_exit:
Packit 5f9837
	if (filename != NULL)
Packit 5f9837
		close(file_descript);
Packit 5f9837
	return rc;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
/*
Packit 5f9837
 * Returns OPT_ERROR on unparsable token.
Packit 5f9837
 */
Packit 5f9837
static int parse_opt_token(const char *token)
Packit 5f9837
{
Packit 5f9837
	if (token == NULL)
Packit 5f9837
		return OPT_ERROR;
Packit 5f9837
Packit 5f9837
	/*
Packit 5f9837
	 * token is NULL terminated and contains exactly the
Packit 5f9837
	 * keyword so we can match exactly
Packit 5f9837
	 */
Packit 5f9837
	if (strcmp(token, "users") == 0)
Packit 5f9837
		return OPT_USERS;
Packit 5f9837
	if (strcmp(token, "user_xattr") == 0)
Packit 5f9837
		return OPT_USER_XATTR;
Packit 5f9837
	if (strcmp(token, "user") == 0 ||
Packit 5f9837
		strcmp(token, "username") == 0)
Packit 5f9837
		return OPT_USER;
Packit 5f9837
	if (strcmp(token, "pass") == 0 ||
Packit 5f9837
		strcmp(token, "password") == 0)
Packit 5f9837
		return OPT_PASS;
Packit 5f9837
	if (strcmp(token, "sec") == 0)
Packit 5f9837
		return OPT_SEC;
Packit 5f9837
	if (strcmp(token, "ip") == 0 ||
Packit 5f9837
		strcmp(token, "addr") == 0)
Packit 5f9837
		return OPT_IP;
Packit 5f9837
	if (strcmp(token, "unc") == 0 ||
Packit 5f9837
		strcmp(token, "target") == 0 ||
Packit 5f9837
		strcmp(token, "path") == 0)
Packit 5f9837
		return OPT_UNC;
Packit 5f9837
	if (strcmp(token, "dom") == 0 ||
Packit 5f9837
		strcmp(token, "domain") == 0 ||
Packit 5f9837
		strcmp(token, "workgroup") == 0)
Packit 5f9837
		return OPT_DOM;
Packit 5f9837
	if (strcmp(token, "cred") == 0 || /* undocumented */
Packit 5f9837
		strcmp(token, "credentials") == 0)
Packit 5f9837
		return OPT_CRED;
Packit 5f9837
	if (strcmp(token, "uid") == 0)
Packit 5f9837
		return OPT_UID;
Packit 5f9837
	if (strcmp(token, "cruid") == 0)
Packit 5f9837
		return OPT_CRUID;
Packit 5f9837
	if (strcmp(token, "gid") == 0)
Packit 5f9837
		return OPT_GID;
Packit 5f9837
	if (strcmp(token, "fmask") == 0)
Packit 5f9837
		return OPT_FMASK;
Packit 5f9837
	if (strcmp(token, "file_mode") == 0)
Packit 5f9837
		return OPT_FILE_MODE;
Packit 5f9837
	if (strcmp(token, "dmask") == 0)
Packit 5f9837
		return OPT_DMASK;
Packit 5f9837
	if (strcmp(token, "dir_mode") == 0 ||
Packit 5f9837
		strcmp(token, "dirm") == 0)
Packit 5f9837
		return OPT_DIR_MODE;
Packit 5f9837
	if (strcmp(token, "nosuid") == 0)
Packit 5f9837
		return OPT_NO_SUID;
Packit 5f9837
	if (strcmp(token, "suid") == 0)
Packit 5f9837
		return OPT_SUID;
Packit 5f9837
	if (strcmp(token, "nodev") == 0)
Packit 5f9837
		return OPT_NO_DEV;
Packit 5f9837
	if (strcmp(token, "nobrl") == 0 ||
Packit 5f9837
		strcmp(token, "nolock") == 0)
Packit 5f9837
		return OPT_NO_LOCK;
Packit 5f9837
	if (strcmp(token, "mand") == 0)
Packit 5f9837
		return OPT_MAND;
Packit 5f9837
	if (strcmp(token, "nomand") == 0)
Packit 5f9837
		return OPT_NOMAND;
Packit 5f9837
	if (strcmp(token, "dev") == 0)
Packit 5f9837
		return OPT_DEV;
Packit 5f9837
	if (strcmp(token, "noexec") == 0)
Packit 5f9837
		return OPT_NO_EXEC;
Packit 5f9837
	if (strcmp(token, "exec") == 0)
Packit 5f9837
		return OPT_EXEC;
Packit 5f9837
	if (strcmp(token, "guest") == 0)
Packit 5f9837
		return OPT_GUEST;
Packit 5f9837
	if (strcmp(token, "ro") == 0)
Packit 5f9837
		return OPT_RO;
Packit 5f9837
	if (strcmp(token, "rw") == 0)
Packit 5f9837
		return OPT_RW;
Packit 5f9837
	if (strcmp(token, "remount") == 0)
Packit 5f9837
		return OPT_REMOUNT;
Packit 5f9837
	if (strcmp(token, "_netdev") == 0)
Packit 5f9837
		return OPT_IGNORE;
Packit 5f9837
	if (strcmp(token, "backupuid") == 0)
Packit 5f9837
		return OPT_BKUPUID;
Packit 5f9837
	if (strcmp(token, "backupgid") == 0)
Packit 5f9837
		return OPT_BKUPGID;
Packit 5f9837
	if (strcmp(token, "nofail") == 0)
Packit 5f9837
		return OPT_NOFAIL;
Packit 5f9837
	if (strncmp(token, "x-", 2) == 0)
Packit 5f9837
		return OPT_IGNORE;
Packit 5f9837
Packit 5f9837
	return OPT_ERROR;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
static int
Packit 5f9837
parse_options(const char *data, struct parsed_mount_info *parsed_info)
Packit 5f9837
{
Packit 5f9837
	char *value = NULL;
Packit 5f9837
	char *equals = NULL;
Packit 5f9837
	char *next_keyword = NULL;
Packit 5f9837
	char *out = parsed_info->options;
Packit 5f9837
	unsigned long *filesys_flags = &parsed_info->flags;
Packit 5f9837
	int out_len = 0;
Packit 5f9837
	int word_len;
Packit 5f9837
	int rc = 0;
Packit 5f9837
	int got_bkupuid = 0;
Packit 5f9837
	int got_bkupgid = 0;
Packit 5f9837
	int got_uid = 0;
Packit 5f9837
	int got_cruid = 0;
Packit 5f9837
	int got_gid = 0;
Packit 5f9837
	uid_t uid, cruid = 0, bkupuid = 0;
Packit 5f9837
	gid_t gid, bkupgid = 0;
Packit 5f9837
	char *ep;
Packit 5f9837
	struct passwd *pw;
Packit 5f9837
	struct group *gr;
Packit 5f9837
	/*
Packit 5f9837
	 * max 32-bit uint in decimal is 4294967295 which is 10 chars wide
Packit 5f9837
	 * +1 for NULL, and +1 for good measure
Packit 5f9837
	 */
Packit 5f9837
	char txtbuf[12];
Packit 5f9837
Packit 5f9837
	/* make sure we're starting from beginning */
Packit 5f9837
	out[0] = '\0';
Packit 5f9837
Packit 5f9837
	/* BB fixme check for separator override BB */
Packit 5f9837
	uid = getuid();
Packit 5f9837
	if (uid != 0)
Packit 5f9837
		got_uid = 1;
Packit 5f9837
Packit 5f9837
	gid = getgid();
Packit 5f9837
	if (gid != 0)
Packit 5f9837
		got_gid = 1;
Packit 5f9837
Packit 5f9837
	if (!data)
Packit 5f9837
		return EX_USAGE;
Packit 5f9837
Packit 5f9837
	/*
Packit 5f9837
	 * format is keyword,keyword2=value2,keyword3=value3... 
Packit 5f9837
	 * data  = next keyword
Packit 5f9837
	 * value = next value ie stuff after equal sign
Packit 5f9837
	 */
Packit 5f9837
	while (data && *data) {
Packit 5f9837
		next_keyword = strchr(data, ',');	/* BB handle sep= */
Packit 5f9837
Packit 5f9837
		/* temporarily null terminate end of keyword=value pair */
Packit 5f9837
		if (next_keyword)
Packit 5f9837
			*next_keyword++ = 0;
Packit 5f9837
Packit 5f9837
		/* temporarily null terminate keyword if there's a value */
Packit 5f9837
		value = NULL;
Packit 5f9837
		if ((equals = strchr(data, '=')) != NULL) {
Packit 5f9837
			*equals = '\0';
Packit 5f9837
			value = equals + 1;
Packit 5f9837
		}
Packit 5f9837
Packit 5f9837
		switch(parse_opt_token(data)) {
Packit 5f9837
		case OPT_USERS:
Packit 5f9837
			if (!value || !*value) {
Packit 5f9837
				*filesys_flags |= MS_USERS;
Packit 5f9837
				goto nocopy;
Packit 5f9837
			}
Packit 5f9837
			break;
Packit 5f9837
Packit 5f9837
		case OPT_USER:
Packit 5f9837
			if (!value || !*value) {
Packit 5f9837
				if (data[4] == '\0') {
Packit 5f9837
					*filesys_flags |= MS_USER;
Packit 5f9837
					goto nocopy;
Packit 5f9837
				} else {
Packit 5f9837
					fprintf(stderr,
Packit 5f9837
						"username specified with no parameter\n");
Packit 5f9837
					return EX_USAGE;
Packit 5f9837
				}
Packit 5f9837
			} else {
Packit 5f9837
				strlcpy(parsed_info->username, value,
Packit 5f9837
					sizeof(parsed_info->username));
Packit 5f9837
				parsed_info->got_user = 1;
Packit 5f9837
				goto nocopy;
Packit 5f9837
			}
Packit 5f9837
Packit 5f9837
		case OPT_PASS:
Packit 5f9837
			if (parsed_info->got_password) {
Packit 5f9837
				fprintf(stderr,
Packit 5f9837
					"password specified twice, ignoring second\n");
Packit 5f9837
				goto nocopy;
Packit 5f9837
			}
Packit 5f9837
			if (!value || !*value) {
Packit 5f9837
				parsed_info->got_password = 1;
Packit 5f9837
				goto nocopy;
Packit 5f9837
			}
Packit 5f9837
			rc = set_password(parsed_info, value);
Packit 5f9837
			if (rc)
Packit 5f9837
				return rc;
Packit 5f9837
			goto nocopy;
Packit 5f9837
Packit 5f9837
		case OPT_SEC:
Packit 5f9837
			if (value) {
Packit 5f9837
				if (!strncmp(value, "none", 4) ||
Packit 5f9837
				    !strncmp(value, "krb5", 4))
Packit 5f9837
					parsed_info->got_password = 1;
Packit 5f9837
			}
Packit 5f9837
			break;
Packit 5f9837
Packit 5f9837
		case OPT_IP:
Packit 5f9837
			if (!value || !*value) {
Packit 5f9837
				fprintf(stderr,
Packit 5f9837
					"target ip address argument missing\n");
Packit 5f9837
			} else if (strnlen(value, MAX_ADDRESS_LEN) <=
Packit 5f9837
				MAX_ADDRESS_LEN) {
Packit 5f9837
				strcpy(parsed_info->addrlist, value);
Packit 5f9837
				if (parsed_info->verboseflag)
Packit 5f9837
					fprintf(stderr,
Packit 5f9837
						"ip address %s override specified\n",
Packit 5f9837
						value);
Packit 5f9837
				goto nocopy;
Packit 5f9837
			} else {
Packit 5f9837
				fprintf(stderr, "ip address too long\n");
Packit 5f9837
				return EX_USAGE;
Packit 5f9837
Packit 5f9837
			}
Packit 5f9837
			break;
Packit 5f9837
Packit 5f9837
		/* unc || target || path */
Packit 5f9837
		case OPT_UNC:
Packit 5f9837
			if (!value || !*value) {
Packit 5f9837
				fprintf(stderr,
Packit 5f9837
					"invalid path to network resource\n");
Packit 5f9837
				return EX_USAGE;
Packit 5f9837
			}
Packit 5f9837
			rc = parse_unc(value, parsed_info);
Packit 5f9837
			if (rc)
Packit 5f9837
				return rc;
Packit 5f9837
			break;
Packit 5f9837
Packit 5f9837
		/* dom || workgroup */
Packit 5f9837
		case OPT_DOM:
Packit 5f9837
			if (!value) {
Packit 5f9837
				/*
Packit 5f9837
				 * An empty domain has been passed
Packit 5f9837
				 */
Packit 5f9837
				/* not necessary but better safe than.. */
Packit 5f9837
				parsed_info->domain[0] = '\0';
Packit 5f9837
				parsed_info->got_domain = 1;
Packit 5f9837
				goto nocopy;
Packit 5f9837
			}
Packit 5f9837
			if (strnlen(value, sizeof(parsed_info->domain)) >=
Packit 5f9837
			    sizeof(parsed_info->domain)) {
Packit 5f9837
				fprintf(stderr, "domain name too long\n");
Packit 5f9837
				return EX_USAGE;
Packit 5f9837
			}
Packit 5f9837
			strlcpy(parsed_info->domain, value,
Packit 5f9837
				sizeof(parsed_info->domain));
Packit 5f9837
			goto nocopy;
Packit 5f9837
Packit 5f9837
		case OPT_CRED:
Packit 5f9837
			if (!value || !*value) {
Packit 5f9837
				fprintf(stderr,
Packit 5f9837
					"invalid credential file name specified\n");
Packit 5f9837
				return EX_USAGE;
Packit 5f9837
			}
Packit 5f9837
			rc = open_cred_file(value, parsed_info);
Packit 5f9837
			if (rc) {
Packit 5f9837
				fprintf(stderr,
Packit 5f9837
					"error %d (%s) opening credential file %s\n",
Packit 5f9837
					rc, strerror(rc), value);
Packit 5f9837
				return rc;
Packit 5f9837
			}
Packit 5f9837
			goto nocopy;
Packit 5f9837
Packit 5f9837
		case OPT_UID:
Packit 5f9837
			if (!value || !*value)
Packit 5f9837
				goto nocopy;
Packit 5f9837
Packit 5f9837
			got_uid = 1;
Packit 5f9837
			pw = getpwnam(value);
Packit 5f9837
			if (pw) {
Packit 5f9837
				uid = pw->pw_uid;
Packit 5f9837
				goto nocopy;
Packit 5f9837
			}
Packit 5f9837
Packit 5f9837
			errno = 0;
Packit 5f9837
			uid = strtoul(value, &ep, 10);
Packit 5f9837
			if (errno == 0 && *ep == '\0')
Packit 5f9837
				goto nocopy;
Packit 5f9837
Packit 5f9837
			fprintf(stderr, "bad option uid=\"%s\"\n", value);
Packit 5f9837
			return EX_USAGE;
Packit 5f9837
		case OPT_CRUID:
Packit 5f9837
			if (!value || !*value)
Packit 5f9837
				goto nocopy;
Packit 5f9837
Packit 5f9837
			got_cruid = 1;
Packit 5f9837
			pw = getpwnam(value);
Packit 5f9837
			if (pw) {
Packit 5f9837
				cruid = pw->pw_uid;
Packit 5f9837
				goto nocopy;
Packit 5f9837
			}
Packit 5f9837
Packit 5f9837
			errno = 0;
Packit 5f9837
			cruid = strtoul(value, &ep, 10);
Packit 5f9837
			if (errno == 0 && *ep == '\0')
Packit 5f9837
				goto nocopy;
Packit 5f9837
Packit 5f9837
			fprintf(stderr, "bad option: cruid=\"%s\"\n", value);
Packit 5f9837
			return EX_USAGE;
Packit 5f9837
		case OPT_GID:
Packit 5f9837
			if (!value || !*value)
Packit 5f9837
				goto nocopy;
Packit 5f9837
Packit 5f9837
			got_gid = 1;
Packit 5f9837
			gr = getgrnam(value);
Packit 5f9837
			if (gr) {
Packit 5f9837
				gid = gr->gr_gid;
Packit 5f9837
				goto nocopy;
Packit 5f9837
			}
Packit 5f9837
Packit 5f9837
			errno = 0;
Packit 5f9837
			gid = strtoul(value, &ep, 10);
Packit 5f9837
			if (errno == 0 && *ep == '\0')
Packit 5f9837
				goto nocopy;
Packit 5f9837
Packit 5f9837
			fprintf(stderr, "bad option: gid=\"%s\"\n", value);
Packit 5f9837
			return EX_USAGE;
Packit 5f9837
		/* fmask falls through to file_mode */
Packit 5f9837
		case OPT_FMASK:
Packit 5f9837
			fprintf(stderr,
Packit 5f9837
				"WARNING: CIFS mount option 'fmask' is\
Packit 5f9837
				 deprecated. Use 'file_mode' instead.\n");
Packit 5f9837
			data = "file_mode";	/* BB fix this */
Packit 5f9837
			/* Fallthrough */
Packit 5f9837
		case OPT_FILE_MODE:
Packit 5f9837
			if (!value || !*value) {
Packit 5f9837
				fprintf(stderr,
Packit 5f9837
					"Option '%s' requires a numerical argument\n",
Packit 5f9837
					data);
Packit 5f9837
				return EX_USAGE;
Packit 5f9837
			}
Packit 5f9837
Packit 5f9837
			if (value[0] != '0')
Packit 5f9837
				fprintf(stderr,
Packit 5f9837
					"WARNING: '%s' not expressed in octal.\n",
Packit 5f9837
					data);
Packit 5f9837
			break;
Packit 5f9837
Packit 5f9837
		/* dmask falls through to dir_mode */
Packit 5f9837
		case OPT_DMASK:
Packit 5f9837
			fprintf(stderr,
Packit 5f9837
				"WARNING: CIFS mount option 'dmask' is\
Packit 5f9837
				 deprecated. Use 'dir_mode' instead.\n");
Packit 5f9837
			data = "dir_mode";
Packit 5f9837
			/* Fallthrough */
Packit 5f9837
		case OPT_DIR_MODE:
Packit 5f9837
			if (!value || !*value) {
Packit 5f9837
				fprintf(stderr,
Packit 5f9837
					"Option '%s' requires a numerical argument\n",
Packit 5f9837
					data);
Packit 5f9837
				return EX_USAGE;
Packit 5f9837
			}
Packit 5f9837
Packit 5f9837
			if (value[0] != '0')
Packit 5f9837
				fprintf(stderr,
Packit 5f9837
					"WARNING: '%s' not expressed in octal.\n",
Packit 5f9837
					data);
Packit 5f9837
			break;
Packit 5f9837
		case OPT_NO_SUID:
Packit 5f9837
			*filesys_flags |= MS_NOSUID;
Packit 5f9837
			goto nocopy;
Packit 5f9837
		case OPT_SUID:
Packit 5f9837
			*filesys_flags &= ~MS_NOSUID;
Packit 5f9837
			goto nocopy;
Packit 5f9837
		case OPT_NO_DEV:
Packit 5f9837
			*filesys_flags |= MS_NODEV;
Packit 5f9837
			goto nocopy;
Packit 5f9837
		case OPT_NO_LOCK:
Packit 5f9837
			*filesys_flags &= ~MS_MANDLOCK;
Packit 5f9837
			break;
Packit 5f9837
		case OPT_MAND:
Packit 5f9837
			*filesys_flags |= MS_MANDLOCK;
Packit 5f9837
			goto nocopy;
Packit 5f9837
		case OPT_NOMAND:
Packit 5f9837
			*filesys_flags &= ~MS_MANDLOCK;
Packit 5f9837
			goto nocopy;
Packit 5f9837
		case OPT_DEV:
Packit 5f9837
			*filesys_flags &= ~MS_NODEV;
Packit 5f9837
			goto nocopy;
Packit 5f9837
		case OPT_NO_EXEC:
Packit 5f9837
			*filesys_flags |= MS_NOEXEC;
Packit 5f9837
			goto nocopy;
Packit 5f9837
		case OPT_EXEC:
Packit 5f9837
			*filesys_flags &= ~MS_NOEXEC;
Packit 5f9837
			goto nocopy;
Packit 5f9837
		case OPT_GUEST:
Packit 5f9837
			parsed_info->got_user = 1;
Packit 5f9837
			parsed_info->got_password = 1;
Packit 5f9837
			goto nocopy;
Packit 5f9837
		case OPT_RO:
Packit 5f9837
			*filesys_flags |= MS_RDONLY;
Packit 5f9837
			goto nocopy;
Packit 5f9837
		case OPT_RW:
Packit 5f9837
			*filesys_flags &= ~MS_RDONLY;
Packit 5f9837
			goto nocopy;
Packit 5f9837
		case OPT_REMOUNT:
Packit 5f9837
			*filesys_flags |= MS_REMOUNT;
Packit 5f9837
			goto nocopy;
Packit 5f9837
		case OPT_IGNORE:
Packit 5f9837
			goto nocopy;
Packit 5f9837
		case OPT_BKUPUID:
Packit 5f9837
			if (!value || !*value)
Packit 5f9837
				goto nocopy;
Packit 5f9837
Packit 5f9837
			got_bkupuid = 1;
Packit 5f9837
			errno = 0;
Packit 5f9837
			bkupuid = strtoul(value, &ep, 10);
Packit 5f9837
			if (errno == 0 && *ep == '\0')
Packit 5f9837
				goto nocopy;
Packit 5f9837
Packit 5f9837
			pw = getpwnam(value);
Packit 5f9837
			if (pw == NULL) {
Packit 5f9837
				fprintf(stderr,
Packit 5f9837
					"bad user name \"%s\"\n", value);
Packit 5f9837
				return EX_USAGE;
Packit 5f9837
			}
Packit 5f9837
Packit 5f9837
			bkupuid = pw->pw_uid;
Packit 5f9837
			goto nocopy;
Packit 5f9837
		case OPT_BKUPGID:
Packit 5f9837
			if (!value || !*value)
Packit 5f9837
				goto nocopy;
Packit 5f9837
Packit 5f9837
			got_bkupgid = 1;
Packit 5f9837
			errno = 0;
Packit 5f9837
			bkupgid = strtoul(value, &ep, 10);
Packit 5f9837
			if (errno == 0 && *ep == '\0')
Packit 5f9837
				goto nocopy;
Packit 5f9837
Packit 5f9837
			gr = getgrnam(value);
Packit 5f9837
			if (gr == NULL) {
Packit 5f9837
				fprintf(stderr,
Packit 5f9837
					"bad group name \"%s\"\n", value);
Packit 5f9837
				return EX_USAGE;
Packit 5f9837
			}
Packit 5f9837
Packit 5f9837
			bkupgid = gr->gr_gid;
Packit 5f9837
			goto nocopy;
Packit 5f9837
		case OPT_NOFAIL:
Packit 5f9837
			parsed_info->nofail = 1;
Packit 5f9837
			goto nocopy;
Packit 5f9837
		}
Packit 5f9837
Packit 5f9837
		/* check size before copying option to buffer */
Packit 5f9837
		word_len = strlen(data);
Packit 5f9837
		if (value)
Packit 5f9837
			word_len += 1 + strlen(value);
Packit 5f9837
Packit 5f9837
		/* need 2 extra bytes for comma and null byte */
Packit 5f9837
		if (out_len + word_len + 2 > MAX_OPTIONS_LEN) {
Packit 5f9837
			fprintf(stderr, "Options string too long\n");
Packit 5f9837
			return EX_USAGE;
Packit 5f9837
		}
Packit 5f9837
Packit 5f9837
		/* put back equals sign, if any */
Packit 5f9837
		if (equals)
Packit 5f9837
			*equals = '=';
Packit 5f9837
Packit 5f9837
		/* go ahead and copy */
Packit 5f9837
		if (out_len)
Packit 5f9837
			strlcat(out, ",", MAX_OPTIONS_LEN);
Packit 5f9837
Packit 5f9837
		strlcat(out, data, MAX_OPTIONS_LEN);
Packit 5f9837
		out_len = strlen(out);
Packit 5f9837
nocopy:
Packit 5f9837
		data = next_keyword;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
Packit 5f9837
	/* special-case the uid and gid */
Packit 5f9837
	if (got_uid) {
Packit 5f9837
		word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", uid);
Packit 5f9837
Packit 5f9837
		/* comma + "uid=" + terminating NULL == 6 */
Packit 5f9837
		if (out_len + word_len + 6 > MAX_OPTIONS_LEN) {
Packit 5f9837
			fprintf(stderr, "Options string too long\n");
Packit 5f9837
			return EX_USAGE;
Packit 5f9837
		}
Packit 5f9837
Packit 5f9837
		if (out_len) {
Packit 5f9837
			strlcat(out, ",", MAX_OPTIONS_LEN);
Packit 5f9837
			out_len++;
Packit 5f9837
		}
Packit 5f9837
		snprintf(out + out_len, word_len + 5, "uid=%s", txtbuf);
Packit 5f9837
		out_len = strlen(out);
Packit 5f9837
	}
Packit 5f9837
	if (got_cruid) {
Packit 5f9837
		word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", cruid);
Packit 5f9837
Packit 5f9837
		/* comma + "cruid=" + terminating NULL == 8 */
Packit 5f9837
		if (out_len + word_len + 8 > MAX_OPTIONS_LEN) {
Packit 5f9837
			fprintf(stderr, "Options string too long\n");
Packit 5f9837
			return EX_USAGE;
Packit 5f9837
		}
Packit 5f9837
Packit 5f9837
		if (out_len) {
Packit 5f9837
			strlcat(out, ",", MAX_OPTIONS_LEN);
Packit 5f9837
			out_len++;
Packit 5f9837
		}
Packit 5f9837
		snprintf(out + out_len, word_len + 7, "cruid=%s", txtbuf);
Packit 5f9837
		out_len = strlen(out);
Packit 5f9837
	}
Packit 5f9837
	if (got_gid) {
Packit 5f9837
		word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", gid);
Packit 5f9837
Packit 5f9837
		/* comma + "gid=" + terminating NULL == 6 */
Packit 5f9837
		if (out_len + word_len + 6 > MAX_OPTIONS_LEN) {
Packit 5f9837
			fprintf(stderr, "Options string too long\n");
Packit 5f9837
			return EX_USAGE;
Packit 5f9837
		}
Packit 5f9837
Packit 5f9837
		if (out_len) {
Packit 5f9837
			strlcat(out, ",", MAX_OPTIONS_LEN);
Packit 5f9837
			out_len++;
Packit 5f9837
		}
Packit 5f9837
		snprintf(out + out_len, word_len + 5, "gid=%s", txtbuf);
Packit 5f9837
	}
Packit 5f9837
	if (got_bkupuid) {
Packit 5f9837
		word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", bkupuid);
Packit 5f9837
Packit 5f9837
		/* comma + "backupuid=" + terminating NULL == 12 */
Packit 5f9837
		if (out_len + word_len + 12 > MAX_OPTIONS_LEN) {
Packit 5f9837
			fprintf(stderr, "Options string too long\n");
Packit 5f9837
			return EX_USAGE;
Packit 5f9837
		}
Packit 5f9837
Packit 5f9837
		if (out_len) {
Packit 5f9837
			strlcat(out, ",", MAX_OPTIONS_LEN);
Packit 5f9837
			out_len++;
Packit 5f9837
		}
Packit 5f9837
		snprintf(out + out_len, word_len + 11, "backupuid=%s", txtbuf);
Packit 5f9837
		out_len = strlen(out);
Packit 5f9837
	}
Packit 5f9837
	if (got_bkupgid) {
Packit 5f9837
		word_len = snprintf(txtbuf, sizeof(txtbuf), "%u", bkupgid);
Packit 5f9837
Packit 5f9837
		/* comma + "backkupgid=" + terminating NULL == 12 */
Packit 5f9837
		if (out_len + word_len + 12 > MAX_OPTIONS_LEN) {
Packit 5f9837
			fprintf(stderr, "Options string too long\n");
Packit 5f9837
			return EX_USAGE;
Packit 5f9837
		}
Packit 5f9837
Packit 5f9837
		if (out_len) {
Packit 5f9837
			strlcat(out, ",", MAX_OPTIONS_LEN);
Packit 5f9837
			out_len++;
Packit 5f9837
		}
Packit 5f9837
		snprintf(out + out_len, word_len + 11, "backupgid=%s", txtbuf);
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	return 0;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
static int parse_unc(const char *unc_name, struct parsed_mount_info *parsed_info)
Packit 5f9837
{
Packit 5f9837
	int length = strnlen(unc_name, MAX_UNC_LEN);
Packit 5f9837
	const char *host, *share, *prepath;
Packit 5f9837
	size_t hostlen, sharelen, prepathlen;
Packit 5f9837
Packit 5f9837
	if (length > (MAX_UNC_LEN - 1)) {
Packit 5f9837
		fprintf(stderr, "mount error: UNC name too long\n");
Packit 5f9837
		return EX_USAGE;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	if (length < 3) {
Packit 5f9837
		fprintf(stderr, "mount error: UNC name too short\n");
Packit 5f9837
		return EX_USAGE;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	if ((strncasecmp("cifs://", unc_name, 7) == 0) ||
Packit 5f9837
	    (strncasecmp("smb://", unc_name, 6) == 0)) {
Packit 5f9837
		fprintf(stderr,
Packit 5f9837
			"Mounting cifs URL not implemented yet. Attempt to mount %s\n",
Packit 5f9837
			unc_name);
Packit 5f9837
		return EX_USAGE;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	if (strncmp(unc_name, "//", 2) && strncmp(unc_name, "\\\\", 2)) {
Packit 5f9837
		fprintf(stderr, "mount.cifs: bad UNC (%s)\n", unc_name);
Packit 5f9837
		return EX_USAGE;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	host = unc_name + 2;
Packit 5f9837
	hostlen = strcspn(host, "/\\");
Packit 5f9837
	if (!hostlen) {
Packit 5f9837
		fprintf(stderr, "mount.cifs: bad UNC (%s)\n", unc_name);
Packit 5f9837
		return EX_USAGE;
Packit 5f9837
	}
Packit 5f9837
	share = host + hostlen + 1;
Packit 5f9837
Packit 5f9837
	if (hostlen + 1 > sizeof(parsed_info->host)) {
Packit 5f9837
		fprintf(stderr, "mount.cifs: host portion of UNC too long\n");
Packit 5f9837
		return EX_USAGE;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	sharelen = strcspn(share, "/\\");
Packit 5f9837
	if (sharelen + 1 > sizeof(parsed_info->share)) {
Packit 5f9837
		fprintf(stderr, "mount.cifs: share portion of UNC too long\n");
Packit 5f9837
		return EX_USAGE;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	prepath = share + sharelen;
Packit 5f9837
	if (*prepath != '\0')
Packit 5f9837
		prepath++;
Packit 5f9837
Packit 5f9837
	prepathlen = strlen(prepath);
Packit 5f9837
Packit 5f9837
	if (prepathlen + 1 > sizeof(parsed_info->prefix)) {
Packit 5f9837
		fprintf(stderr, "mount.cifs: UNC prefixpath too long\n");
Packit 5f9837
		return EX_USAGE;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	/* copy pieces into their resepective buffers */
Packit 5f9837
	memcpy(parsed_info->host, host, hostlen);
Packit 5f9837
	memcpy(parsed_info->share, share, sharelen);
Packit 5f9837
	memcpy(parsed_info->prefix, prepath, prepathlen);
Packit 5f9837
Packit 5f9837
	return 0;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
static int get_pw_from_env(struct parsed_mount_info *parsed_info)
Packit 5f9837
{
Packit 5f9837
	int rc = 0;
Packit 5f9837
Packit 5f9837
	if (getenv("PASSWD"))
Packit 5f9837
		rc = set_password(parsed_info, getenv("PASSWD"));
Packit 5f9837
	else if (getenv("PASSWD_FD"))
Packit 5f9837
		rc = get_password_from_file(atoi(getenv("PASSWD_FD")), NULL,
Packit 5f9837
					    parsed_info);
Packit 5f9837
	else if (getenv("PASSWD_FILE"))
Packit 5f9837
		rc = get_password_from_file(0, getenv("PASSWD_FILE"),
Packit 5f9837
					    parsed_info);
Packit 5f9837
Packit 5f9837
	return rc;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
static struct option longopts[] = {
Packit 5f9837
	{"all", 0, NULL, 'a'},
Packit 5f9837
	{"help", 0, NULL, 'h'},
Packit 5f9837
	{"move", 0, NULL, 'm'},
Packit 5f9837
	{"bind", 0, NULL, 'b'},
Packit 5f9837
	{"read-only", 0, NULL, 'r'},
Packit 5f9837
	{"ro", 0, NULL, 'r'},
Packit 5f9837
	{"verbose", 0, NULL, 'v'},
Packit 5f9837
	{"version", 0, NULL, 'V'},
Packit 5f9837
	{"read-write", 0, NULL, 'w'},
Packit 5f9837
	{"rw", 0, NULL, 'w'},
Packit 5f9837
	{"options", 1, NULL, 'o'},
Packit 5f9837
	{"type", 1, NULL, 't'},
Packit 5f9837
	{"uid", 1, NULL, '1'},
Packit 5f9837
	{"gid", 1, NULL, '2'},
Packit 5f9837
	{"user", 1, NULL, 'u'},
Packit 5f9837
	{"username", 1, NULL, 'u'},
Packit 5f9837
	{"dom", 1, NULL, 'd'},
Packit 5f9837
	{"domain", 1, NULL, 'd'},
Packit 5f9837
	{"password", 1, NULL, 'p'},
Packit 5f9837
	{"pass", 1, NULL, 'p'},
Packit 5f9837
	{"credentials", 1, NULL, 'c'},
Packit 5f9837
	{"port", 1, NULL, 'P'},
Packit 5f9837
	{"sloppy", 0, NULL, 's'},
Packit 5f9837
	{NULL, 0, NULL, 0}
Packit 5f9837
};
Packit 5f9837
Packit 5f9837
/* convert a string to uppercase. return false if the string
Packit 5f9837
 * wasn't ASCII. Return success on a NULL ptr */
Packit 5f9837
static int uppercase_string(char *string)
Packit 5f9837
{
Packit 5f9837
	if (!string)
Packit 5f9837
		return 1;
Packit 5f9837
Packit 5f9837
	while (*string) {
Packit 5f9837
		/* check for unicode */
Packit 5f9837
		if ((unsigned char)string[0] & 0x80)
Packit 5f9837
			return 0;
Packit 5f9837
		*string = toupper((unsigned char)*string);
Packit 5f9837
		string++;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	return 1;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
static void print_cifs_mount_version(void)
Packit 5f9837
{
Packit 5f9837
	printf("mount.cifs version: %s\n", VERSION);
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
/*
Packit 5f9837
 * This function borrowed from fuse-utils...
Packit 5f9837
 *
Packit 5f9837
 * glibc's addmntent (at least as of 2.10 or so) doesn't properly encode
Packit 5f9837
 * newlines embedded within the text fields. To make sure no one corrupts
Packit 5f9837
 * the mtab, fail the mount if there are embedded newlines.
Packit 5f9837
 */
Packit 5f9837
static int check_newline(const char *progname, const char *name)
Packit 5f9837
{
Packit 5f9837
	const char *s;
Packit 5f9837
	for (s = "\n"; *s; s++) {
Packit 5f9837
		if (strchr(name, *s)) {
Packit 5f9837
			fprintf(stderr,
Packit 5f9837
				"%s: illegal character 0x%02x in mount entry\n",
Packit 5f9837
				progname, *s);
Packit 5f9837
			return EX_USAGE;
Packit 5f9837
		}
Packit 5f9837
	}
Packit 5f9837
	return 0;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
static int check_mtab(const char *progname, const char *devname,
Packit 5f9837
		      const char *dir)
Packit 5f9837
{
Packit 5f9837
	if (check_newline(progname, devname) || check_newline(progname, dir))
Packit 5f9837
		return EX_USAGE;
Packit 5f9837
	return 0;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
static int
Packit 5f9837
add_mtab(char *devname, char *mountpoint, unsigned long flags, const char *fstype)
Packit 5f9837
{
Packit 5f9837
	int rc = 0, tmprc, fd;
Packit 5f9837
	uid_t uid;
Packit 5f9837
	char *mount_user = NULL;
Packit 5f9837
	struct mntent mountent;
Packit 5f9837
	struct stat statbuf;
Packit 5f9837
	FILE *pmntfile;
Packit 5f9837
	sigset_t mask, oldmask;
Packit 5f9837
Packit 5f9837
	uid = getuid();
Packit 5f9837
	if (uid != 0)
Packit 5f9837
		mount_user = getusername(uid);
Packit 5f9837
Packit 5f9837
	/*
Packit 5f9837
	 * Set the real uid to the effective uid. This prevents unprivileged
Packit 5f9837
	 * users from sending signals to this process, though ^c on controlling
Packit 5f9837
	 * terminal should still work.
Packit 5f9837
	 */
Packit 5f9837
	rc = setreuid(geteuid(), -1);
Packit 5f9837
	if (rc != 0) {
Packit 5f9837
		fprintf(stderr, "Unable to set real uid to effective uid: %s\n",
Packit 5f9837
				strerror(errno));
Packit 5f9837
		return EX_FILEIO;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	rc = sigfillset(&mask);
Packit 5f9837
	if (rc) {
Packit 5f9837
		fprintf(stderr, "Unable to set filled signal mask\n");
Packit 5f9837
		return EX_FILEIO;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	rc = sigprocmask(SIG_SETMASK, &mask, &oldmask);
Packit 5f9837
	if (rc) {
Packit 5f9837
		fprintf(stderr, "Unable to make process ignore signals\n");
Packit 5f9837
		return EX_FILEIO;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	rc = toggle_dac_capability(1, 1);
Packit 5f9837
	if (rc)
Packit 5f9837
		return EX_FILEIO;
Packit 5f9837
Packit 5f9837
	atexit(unlock_mtab);
Packit 5f9837
	rc = lock_mtab();
Packit 5f9837
	if (rc) {
Packit 5f9837
		fprintf(stderr, "cannot lock mtab");
Packit 5f9837
		rc = EX_FILEIO;
Packit 5f9837
		goto add_mtab_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	pmntfile = setmntent(MOUNTED, "a+");
Packit 5f9837
	if (!pmntfile) {
Packit 5f9837
		fprintf(stderr, "could not update mount table\n");
Packit 5f9837
		unlock_mtab();
Packit 5f9837
		rc = EX_FILEIO;
Packit 5f9837
		goto add_mtab_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	fd = fileno(pmntfile);
Packit 5f9837
	if (fd < 0) {
Packit 5f9837
		fprintf(stderr, "mntent does not appear to be valid\n");
Packit 5f9837
		unlock_mtab();
Packit 5f9837
		rc = EX_FILEIO;
Packit 5f9837
		goto add_mtab_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	rc = fstat(fd, &statbuf);
Packit 5f9837
	if (rc != 0) {
Packit 5f9837
		fprintf(stderr, "unable to fstat open mtab\n");
Packit 5f9837
		endmntent(pmntfile);
Packit 5f9837
		unlock_mtab();
Packit 5f9837
		rc = EX_FILEIO;
Packit 5f9837
		goto add_mtab_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	mountent.mnt_fsname = devname;
Packit 5f9837
	mountent.mnt_dir = mountpoint;
Packit 5f9837
	mountent.mnt_type = (char *)(void *)fstype;
Packit 5f9837
	mountent.mnt_opts = (char *)calloc(MTAB_OPTIONS_LEN, 1);
Packit 5f9837
	if (mountent.mnt_opts) {
Packit 5f9837
		if (flags & MS_RDONLY)
Packit 5f9837
			strlcat(mountent.mnt_opts, "ro", MTAB_OPTIONS_LEN);
Packit 5f9837
		else
Packit 5f9837
			strlcat(mountent.mnt_opts, "rw", MTAB_OPTIONS_LEN);
Packit 5f9837
Packit 5f9837
		if (flags & MS_MANDLOCK)
Packit 5f9837
			strlcat(mountent.mnt_opts, ",mand", MTAB_OPTIONS_LEN);
Packit 5f9837
		if (flags & MS_NOEXEC)
Packit 5f9837
			strlcat(mountent.mnt_opts, ",noexec", MTAB_OPTIONS_LEN);
Packit 5f9837
		if (flags & MS_NOSUID)
Packit 5f9837
			strlcat(mountent.mnt_opts, ",nosuid", MTAB_OPTIONS_LEN);
Packit 5f9837
		if (flags & MS_NODEV)
Packit 5f9837
			strlcat(mountent.mnt_opts, ",nodev", MTAB_OPTIONS_LEN);
Packit 5f9837
		if (flags & MS_SYNCHRONOUS)
Packit 5f9837
			strlcat(mountent.mnt_opts, ",sync", MTAB_OPTIONS_LEN);
Packit 5f9837
		if (mount_user) {
Packit 5f9837
			strlcat(mountent.mnt_opts, ",user=", MTAB_OPTIONS_LEN);
Packit 5f9837
			strlcat(mountent.mnt_opts, mount_user,
Packit 5f9837
				MTAB_OPTIONS_LEN);
Packit 5f9837
		}
Packit 5f9837
	}
Packit 5f9837
	mountent.mnt_freq = 0;
Packit 5f9837
	mountent.mnt_passno = 0;
Packit 5f9837
	rc = addmntent(pmntfile, &mountent);
Packit 5f9837
	if (rc) {
Packit 5f9837
		int ignore __attribute__((unused));
Packit 5f9837
Packit 5f9837
		fprintf(stderr, "unable to add mount entry to mtab\n");
Packit 5f9837
		ignore = ftruncate(fd, statbuf.st_size);
Packit 5f9837
		rc = EX_FILEIO;
Packit 5f9837
	}
Packit 5f9837
	tmprc = my_endmntent(pmntfile, statbuf.st_size);
Packit 5f9837
	if (tmprc) {
Packit 5f9837
		fprintf(stderr, "error %d detected on close of mtab\n", tmprc);
Packit 5f9837
		rc = EX_FILEIO;
Packit 5f9837
	}
Packit 5f9837
	unlock_mtab();
Packit 5f9837
	free(mountent.mnt_opts);
Packit 5f9837
add_mtab_exit:
Packit 5f9837
	toggle_dac_capability(1, 0);
Packit 5f9837
	sigprocmask(SIG_SETMASK, &oldmask, NULL);
Packit 5f9837
Packit 5f9837
	return rc;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
static int
Packit 5f9837
del_mtab(char *mountpoint)
Packit 5f9837
{
Packit 5f9837
	int len, tmprc, rc = 0;
Packit 5f9837
	FILE *mnttmp, *mntmtab;
Packit 5f9837
	struct mntent *mountent;
Packit 5f9837
	char *mtabfile, *mtabdir, *mtabtmpfile = NULL;
Packit 5f9837
Packit 5f9837
	mtabfile = strdup(MOUNTED);
Packit 5f9837
	if (!mtabfile) {
Packit 5f9837
		fprintf(stderr, "del_mtab: cannot strdup MOUNTED\n");
Packit 5f9837
		rc = EX_FILEIO;
Packit 5f9837
		goto del_mtab_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	mtabdir = dirname(mtabfile);
Packit 5f9837
	len = strlen(mtabdir) + strlen(MNT_TMP_FILE);
Packit 5f9837
	mtabtmpfile = malloc(len + 1);
Packit 5f9837
	if (!mtabtmpfile) {
Packit 5f9837
		fprintf(stderr, "del_mtab: cannot allocate memory to tmp file\n");
Packit 5f9837
		rc = EX_FILEIO;
Packit 5f9837
		goto del_mtab_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	if (sprintf(mtabtmpfile, "%s%s", mtabdir, MNT_TMP_FILE) != len) {
Packit 5f9837
		fprintf(stderr, "del_mtab: error writing new string\n");
Packit 5f9837
		rc = EX_FILEIO;
Packit 5f9837
		goto del_mtab_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	atexit(unlock_mtab);
Packit 5f9837
	rc = lock_mtab();
Packit 5f9837
	if (rc) {
Packit 5f9837
		fprintf(stderr, "del_mtab: cannot lock mtab\n");
Packit 5f9837
		rc = EX_FILEIO;
Packit 5f9837
		goto del_mtab_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	mtabtmpfile = mktemp(mtabtmpfile);
Packit 5f9837
	if (!mtabtmpfile) {
Packit 5f9837
		fprintf(stderr, "del_mtab: cannot setup tmp file destination\n");
Packit 5f9837
		rc = EX_FILEIO;
Packit 5f9837
		goto del_mtab_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	mntmtab = setmntent(MOUNTED, "r");
Packit 5f9837
	if (!mntmtab) {
Packit 5f9837
		fprintf(stderr, "del_mtab: could not update mount table\n");
Packit 5f9837
		rc = EX_FILEIO;
Packit 5f9837
		goto del_mtab_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	mnttmp = setmntent(mtabtmpfile, "w");
Packit 5f9837
	if (!mnttmp) {
Packit 5f9837
		fprintf(stderr, "del_mtab: could not update mount table\n");
Packit 5f9837
		endmntent(mntmtab);
Packit 5f9837
		rc = EX_FILEIO;
Packit 5f9837
		goto del_mtab_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	while ((mountent = getmntent(mntmtab)) != NULL) {
Packit 5f9837
		if (!strcmp(mountent->mnt_dir, mountpoint))
Packit 5f9837
			continue;
Packit 5f9837
		rc = addmntent(mnttmp, mountent);
Packit 5f9837
		if (rc) {
Packit 5f9837
			fprintf(stderr, "del_mtab: unable to add mount entry to mtab\n");
Packit 5f9837
			rc = EX_FILEIO;
Packit 5f9837
			goto del_mtab_error;
Packit 5f9837
		}
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	endmntent(mntmtab);
Packit 5f9837
Packit 5f9837
	tmprc = my_endmntent(mnttmp, 0);
Packit 5f9837
	if (tmprc) {
Packit 5f9837
		fprintf(stderr, "del_mtab: error %d detected on close of tmp file\n", tmprc);
Packit 5f9837
		rc = EX_FILEIO;
Packit 5f9837
		goto del_mtab_error;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	if (rename(mtabtmpfile, MOUNTED)) {
Packit 5f9837
		fprintf(stderr, "del_mtab: error %d when renaming mtab in place\n", errno);
Packit 5f9837
		rc = EX_FILEIO;
Packit 5f9837
		goto del_mtab_error;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
del_mtab_exit:
Packit 5f9837
	unlock_mtab();
Packit 5f9837
	free(mtabtmpfile);
Packit 5f9837
	free(mtabfile);
Packit 5f9837
	return rc;
Packit 5f9837
Packit 5f9837
del_mtab_error:
Packit 5f9837
	if (unlink(mtabtmpfile))
Packit 5f9837
		fprintf(stderr, "del_mtab: failed to delete tmp file - %s\n",
Packit 5f9837
				strerror(errno));
Packit 5f9837
	goto del_mtab_exit;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
/* have the child drop root privileges */
Packit 5f9837
static int
Packit 5f9837
drop_child_privs(void)
Packit 5f9837
{
Packit 5f9837
	int rc;
Packit 5f9837
	uid_t uid = getuid();
Packit 5f9837
	gid_t gid = getgid();
Packit 5f9837
Packit 5f9837
	if (gid) {
Packit 5f9837
		rc = setgid(gid);
Packit 5f9837
		if (rc) {
Packit 5f9837
			fprintf(stderr, "Unable set group identity: %s\n",
Packit 5f9837
					strerror(errno));
Packit 5f9837
			return EX_SYSERR;
Packit 5f9837
		}
Packit 5f9837
	}
Packit 5f9837
	if (uid) {
Packit 5f9837
		rc = setuid(uid);
Packit 5f9837
		if (rc) {
Packit 5f9837
			fprintf(stderr, "Unable set user identity: %s\n",
Packit 5f9837
					strerror(errno));
Packit 5f9837
			return EX_SYSERR;
Packit 5f9837
		}
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	return 0;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
/*
Packit 5f9837
 * If systemd is running and systemd-ask-password --
Packit 5f9837
 * is available, then use that else fallback on getpass(..)
Packit 5f9837
 *
Packit 5f9837
 * Returns: @input or NULL on error
Packit 5f9837
 */
Packit 5f9837
static char*
Packit 5f9837
get_password(const char *prompt, char *input, int capacity)
Packit 5f9837
{
Packit 5f9837
#ifdef ENABLE_SYSTEMD
Packit 5f9837
	int is_systemd_running;
Packit 5f9837
	struct stat a, b;
Packit 5f9837
Packit 5f9837
	/* We simply test whether the systemd cgroup hierarchy is
Packit 5f9837
	 * mounted */
Packit 5f9837
	is_systemd_running = (lstat("/sys/fs/cgroup", &a) == 0)
Packit 5f9837
		&& (lstat("/sys/fs/cgroup/systemd", &b) == 0)
Packit 5f9837
		&& (a.st_dev != b.st_dev);
Packit 5f9837
Packit 5f9837
	if (is_systemd_running) {
Packit 5f9837
		char *cmd, *ret;
Packit 5f9837
		FILE *ask_pass_fp = NULL;
Packit 5f9837
Packit 5f9837
		cmd = ret = NULL;
Packit 5f9837
		if (asprintf(&cmd, "systemd-ask-password \"%s\"", prompt) >= 0) {
Packit 5f9837
			ask_pass_fp = popen (cmd, "re");
Packit 5f9837
			free (cmd);
Packit 5f9837
		}
Packit 5f9837
Packit 5f9837
		if (ask_pass_fp) {
Packit 5f9837
			ret = fgets(input, capacity, ask_pass_fp);
Packit 5f9837
			pclose(ask_pass_fp);
Packit 5f9837
		}
Packit 5f9837
Packit 5f9837
		if (ret) {
Packit 5f9837
			int len = strlen(input);
Packit 5f9837
			if (input[len - 1] == '\n')
Packit 5f9837
				input[len - 1] = '\0';
Packit 5f9837
			return input;
Packit 5f9837
		}
Packit 5f9837
	}
Packit 5f9837
#endif
Packit 5f9837
Packit 5f9837
	/*
Packit 5f9837
	 * Falling back to getpass(..)
Packit 5f9837
	 * getpass is obsolete, but there's apparently nothing that replaces it
Packit 5f9837
	 */
Packit 5f9837
	char *tmp_pass = getpass(prompt);
Packit 5f9837
	if (!tmp_pass)
Packit 5f9837
		return NULL;
Packit 5f9837
Packit 5f9837
	strncpy(input, tmp_pass, capacity - 1);
Packit 5f9837
	input[capacity - 1] = '\0';
Packit 5f9837
Packit 5f9837
	/* zero-out the static buffer */
Packit 5f9837
	memset(tmp_pass, 0, strlen(tmp_pass));
Packit 5f9837
Packit 5f9837
	return input;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
static int
Packit 5f9837
assemble_mountinfo(struct parsed_mount_info *parsed_info,
Packit 5f9837
		   const char *thisprogram, const char *mountpoint,
Packit 5f9837
		   const char *orig_dev, char *orgoptions)
Packit 5f9837
{
Packit 5f9837
	int rc;
Packit 5f9837
Packit 5f9837
	rc = drop_capabilities(0);
Packit 5f9837
	if (rc)
Packit 5f9837
		goto assemble_exit;
Packit 5f9837
Packit 5f9837
	rc = drop_child_privs();
Packit 5f9837
	if (rc)
Packit 5f9837
		goto assemble_exit;
Packit 5f9837
Packit 5f9837
	if (getuid()) {
Packit 5f9837
		rc = check_fstab(thisprogram, mountpoint, orig_dev,
Packit 5f9837
				 &orgoptions);
Packit 5f9837
		if (rc)
Packit 5f9837
			goto assemble_exit;
Packit 5f9837
Packit 5f9837
		/* enable any default user mount flags */
Packit 5f9837
		parsed_info->flags |= CIFS_SETUID_FLAGS;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	rc = get_pw_from_env(parsed_info);
Packit 5f9837
	if (rc)
Packit 5f9837
		goto assemble_exit;
Packit 5f9837
Packit 5f9837
	if (orgoptions) {
Packit 5f9837
		rc = parse_options(orgoptions, parsed_info);
Packit 5f9837
		if (rc)
Packit 5f9837
			goto assemble_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	if (getuid()) {
Packit 5f9837
		if (!(parsed_info->flags & (MS_USERS | MS_USER))) {
Packit 5f9837
			fprintf(stderr, "%s: permission denied\n", thisprogram);
Packit 5f9837
			rc = EX_USAGE;
Packit 5f9837
			goto assemble_exit;
Packit 5f9837
		}
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	parsed_info->flags &= ~(MS_USERS | MS_USER);
Packit 5f9837
Packit 5f9837
	rc = parse_unc(orig_dev, parsed_info);
Packit 5f9837
	if (rc)
Packit 5f9837
		goto assemble_exit;
Packit 5f9837
Packit 5f9837
	if (parsed_info->addrlist[0] == '\0')
Packit 5f9837
		rc = resolve_host(parsed_info->host, parsed_info->addrlist);
Packit 5f9837
Packit 5f9837
	switch (rc) {
Packit 5f9837
	case EX_USAGE:
Packit 5f9837
		fprintf(stderr, "mount error: could not resolve address for "
Packit 5f9837
			"%s: %s\n", parsed_info->host,
Packit 5f9837
			rc == EAI_SYSTEM ? strerror(errno) : gai_strerror(rc));
Packit 5f9837
		goto assemble_exit;
Packit 5f9837
Packit 5f9837
	case EX_SYSERR:
Packit 5f9837
		fprintf(stderr, "mount error: problem parsing address "
Packit 5f9837
			"list: %s\n", strerror(errno));
Packit 5f9837
		goto assemble_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	if (!parsed_info->got_user) {
Packit 5f9837
		/*
Packit 5f9837
		 * Note that the password will not be retrieved from the
Packit 5f9837
		 * USER env variable (ie user%password form) as there is
Packit 5f9837
		 * already a PASSWD environment varaible
Packit 5f9837
		 */
Packit 5f9837
		if (getenv("USER"))
Packit 5f9837
			strlcpy(parsed_info->username, getenv("USER"),
Packit 5f9837
				sizeof(parsed_info->username));
Packit 5f9837
		else
Packit 5f9837
			strlcpy(parsed_info->username, getusername(getuid()),
Packit 5f9837
				sizeof(parsed_info->username));
Packit 5f9837
		parsed_info->got_user = 1;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	if (!parsed_info->got_password) {
Packit 5f9837
		char tmp_pass[MOUNT_PASSWD_SIZE + 1];
Packit 5f9837
		char *prompt = NULL;
Packit 5f9837
Packit 5f9837
		if(asprintf(&prompt, "Password for %s@%s: ", parsed_info->username, orig_dev) < 0)
Packit 5f9837
			prompt = NULL;
Packit 5f9837
Packit 5f9837
		if (get_password(prompt ? prompt : "Password: ", tmp_pass, MOUNT_PASSWD_SIZE + 1)) {
Packit 5f9837
			rc = set_password(parsed_info, tmp_pass);
Packit 5f9837
		} else {
Packit 5f9837
			fprintf(stderr, "Error reading password, exiting\n");
Packit 5f9837
			rc = EX_SYSERR;
Packit 5f9837
		}
Packit 5f9837
Packit 5f9837
		free(prompt);
Packit 5f9837
		if (rc)
Packit 5f9837
			goto assemble_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	/* copy in user= string */
Packit 5f9837
	if (parsed_info->got_user) {
Packit 5f9837
		if (*parsed_info->options)
Packit 5f9837
			strlcat(parsed_info->options, ",",
Packit 5f9837
				sizeof(parsed_info->options));
Packit 5f9837
		strlcat(parsed_info->options, "user=",
Packit 5f9837
			sizeof(parsed_info->options));
Packit 5f9837
		strlcat(parsed_info->options, parsed_info->username,
Packit 5f9837
			sizeof(parsed_info->options));
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	if (*parsed_info->domain) {
Packit 5f9837
		if (*parsed_info->options)
Packit 5f9837
			strlcat(parsed_info->options, ",",
Packit 5f9837
				sizeof(parsed_info->options));
Packit 5f9837
		strlcat(parsed_info->options, "domain=",
Packit 5f9837
			sizeof(parsed_info->options));
Packit 5f9837
		strlcat(parsed_info->options, parsed_info->domain,
Packit 5f9837
			sizeof(parsed_info->options));
Packit 5f9837
	} else if (parsed_info->got_domain) {
Packit 5f9837
		strlcat(parsed_info->options, ",domain=",
Packit 5f9837
			sizeof(parsed_info->options));
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
assemble_exit:
Packit 5f9837
	return rc;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
/*
Packit 5f9837
 * chdir() into the mountpoint and determine "realpath". We assume here that
Packit 5f9837
 * "mountpoint" is a statically allocated string and does not need to be freed.
Packit 5f9837
 */
Packit 5f9837
static int
Packit 5f9837
acquire_mountpoint(char **mountpointp)
Packit 5f9837
{
Packit 5f9837
	int rc, dacrc;
Packit 5f9837
	uid_t realuid, oldfsuid;
Packit 5f9837
	gid_t oldfsgid;
Packit 5f9837
	char *mountpoint;
Packit 5f9837
Packit 5f9837
	/*
Packit 5f9837
	 * Acquire the necessary privileges to chdir to the mountpoint. If
Packit 5f9837
	 * the real uid is root, then we reacquire CAP_DAC_READ_SEARCH. If
Packit 5f9837
	 * it's not, then we change the fsuid to the real uid to ensure that
Packit 5f9837
	 * the mounting user actually has access to the mountpoint.
Packit 5f9837
	 *
Packit 5f9837
	 * The mount(8) manpage does not state that users must be able to
Packit 5f9837
	 * chdir into the mountpoint in order to mount onto it, but if we
Packit 5f9837
	 * allow that, then an unprivileged user could use this program to
Packit 5f9837
	 * "probe" into directories to which he does not have access.
Packit 5f9837
	 */
Packit 5f9837
	realuid = getuid();
Packit 5f9837
	if (realuid == 0) {
Packit 5f9837
		dacrc = toggle_dac_capability(0, 1);
Packit 5f9837
		if (dacrc)
Packit 5f9837
			return dacrc;
Packit 5f9837
	} else {
Packit 5f9837
		oldfsuid = setfsuid(realuid);
Packit 5f9837
		oldfsgid = setfsgid(getgid());
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	rc = chdir(*mountpointp);
Packit 5f9837
	if (rc) {
Packit 5f9837
		fprintf(stderr, "Couldn't chdir to %s: %s\n", *mountpointp,
Packit 5f9837
			strerror(errno));
Packit 5f9837
		rc = EX_USAGE;
Packit 5f9837
		goto restore_privs;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	mountpoint = realpath(".", NULL);
Packit 5f9837
	if (!mountpoint) {
Packit 5f9837
		fprintf(stderr, "Unable to resolve %s to canonical path: %s\n",
Packit 5f9837
			*mountpointp, strerror(errno));
Packit 5f9837
		rc = EX_SYSERR;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	*mountpointp = mountpoint;
Packit 5f9837
restore_privs:
Packit 5f9837
	if (realuid == 0) {
Packit 5f9837
		dacrc = toggle_dac_capability(0, 0);
Packit 5f9837
		if (dacrc)
Packit 5f9837
			rc = rc ? rc : dacrc;
Packit 5f9837
	} else {
Packit 5f9837
		uid_t __attribute__((unused)) uignore = setfsuid(oldfsuid);
Packit 5f9837
		gid_t __attribute__((unused)) gignore = setfsgid(oldfsgid);
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	return rc;
Packit 5f9837
}
Packit 5f9837
Packit 5f9837
int main(int argc, char **argv)
Packit 5f9837
{
Packit 5f9837
	int c;
Packit 5f9837
	char *orgoptions = NULL;
Packit 5f9837
	char *mountpoint = NULL;
Packit 5f9837
	char *options = NULL;
Packit 5f9837
	char *orig_dev = NULL;
Packit 5f9837
	char *currentaddress, *nextaddress;
Packit 5f9837
	int rc = 0;
Packit 5f9837
	int already_uppercased = 0;
Packit 5f9837
	int sloppy = 0;
Packit 5f9837
	size_t options_size = MAX_OPTIONS_LEN;
Packit 5f9837
	struct parsed_mount_info *parsed_info = NULL;
Packit 5f9837
	pid_t pid;
Packit 5f9837
Packit 5f9837
	rc = check_setuid();
Packit 5f9837
	if (rc)
Packit 5f9837
		return rc;
Packit 5f9837
Packit 5f9837
	rc = drop_capabilities(1);
Packit 5f9837
	if (rc)
Packit 5f9837
		return EX_SYSERR;
Packit 5f9837
Packit 5f9837
	/* setlocale(LC_ALL, "");
Packit 5f9837
	   bindtextdomain(PACKAGE, LOCALEDIR);
Packit 5f9837
	   textdomain(PACKAGE); */
Packit 5f9837
Packit 5f9837
	if (!argc || !argv) {
Packit 5f9837
		rc = mount_usage(stderr);
Packit 5f9837
		goto mount_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	thisprogram = basename(argv[0]);
Packit 5f9837
	if (thisprogram == NULL)
Packit 5f9837
		thisprogram = "mount.cifs";
Packit 5f9837
Packit 5f9837
	/* allocate parsed_info as shared anonymous memory range */
Packit 5f9837
	parsed_info = mmap((void *)0, sizeof(*parsed_info), PROT_READ | PROT_WRITE,
Packit 5f9837
			   MAP_ANONYMOUS | MAP_SHARED, -1, 0);
Packit 5f9837
	if (parsed_info == (struct parsed_mount_info *) -1) {
Packit 5f9837
		parsed_info = NULL;
Packit 5f9837
		fprintf(stderr, "Unable to allocate memory: %s\n",
Packit 5f9837
				strerror(errno));
Packit 5f9837
		return EX_SYSERR;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	/* add sharename in opts string as unc= parm */
Packit 5f9837
	while ((c = getopt_long(argc, argv, "?fhno:rsvVw",
Packit 5f9837
				longopts, NULL)) != -1) {
Packit 5f9837
		switch (c) {
Packit 5f9837
		case '?':
Packit 5f9837
		case 'h':	/* help */
Packit 5f9837
			rc = mount_usage(stdout);
Packit 5f9837
			goto mount_exit;
Packit 5f9837
		case 'n':
Packit 5f9837
			++parsed_info->nomtab;
Packit 5f9837
			break;
Packit 5f9837
		case 'o':
Packit 5f9837
			orgoptions = strndup(optarg, MAX_OPTIONS_LEN);
Packit 5f9837
			if (!orgoptions) {
Packit 5f9837
				rc = EX_SYSERR;
Packit 5f9837
				goto mount_exit;
Packit 5f9837
			}
Packit 5f9837
			break;
Packit 5f9837
		case 'r':	/* mount readonly */
Packit 5f9837
			parsed_info->flags |= MS_RDONLY;
Packit 5f9837
			break;
Packit 5f9837
		case 'v':
Packit 5f9837
			++parsed_info->verboseflag;
Packit 5f9837
			break;
Packit 5f9837
		case 'V':
Packit 5f9837
			print_cifs_mount_version();
Packit 5f9837
			exit(0);
Packit 5f9837
		case 'w':
Packit 5f9837
			parsed_info->flags &= ~MS_RDONLY;
Packit 5f9837
			break;
Packit 5f9837
		case 'f':
Packit 5f9837
			++parsed_info->fakemnt;
Packit 5f9837
			break;
Packit 5f9837
		case 's':
Packit 5f9837
			++sloppy;
Packit 5f9837
			break;
Packit 5f9837
		default:
Packit 5f9837
			fprintf(stderr, "unknown command-line option: %c\n", c);
Packit 5f9837
			rc = mount_usage(stderr);
Packit 5f9837
			goto mount_exit;
Packit 5f9837
		}
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	if (argc < optind + 2) {
Packit 5f9837
		rc = mount_usage(stderr);
Packit 5f9837
		goto mount_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	orig_dev = argv[optind];
Packit 5f9837
	mountpoint = argv[optind + 1];
Packit 5f9837
Packit 5f9837
	/* chdir into mountpoint as soon as possible */
Packit 5f9837
	rc = acquire_mountpoint(&mountpoint);
Packit 5f9837
	if (rc)
Packit 5f9837
		return rc;
Packit 5f9837
Packit 5f9837
	/*
Packit 5f9837
	 * mount.cifs does privilege separation. Most of the code to handle
Packit 5f9837
	 * assembling the mount info is done in a child process that drops
Packit 5f9837
	 * privileges. The info is assembled in parsed_info which is a
Packit 5f9837
	 * shared, mmaped memory segment. The parent waits for the child to
Packit 5f9837
	 * exit and checks the return code. If it's anything but "0", then
Packit 5f9837
	 * the process exits without attempting anything further.
Packit 5f9837
	 */
Packit 5f9837
	pid = fork();
Packit 5f9837
	if (pid == -1) {
Packit 5f9837
		fprintf(stderr, "Unable to fork: %s\n", strerror(errno));
Packit 5f9837
		rc = EX_SYSERR;
Packit 5f9837
		goto mount_exit;
Packit 5f9837
	} else if (!pid) {
Packit 5f9837
		/* child */
Packit 5f9837
		rc = assemble_mountinfo(parsed_info, thisprogram, mountpoint,
Packit 5f9837
					orig_dev, orgoptions);
Packit 5f9837
		return rc;
Packit 5f9837
	} else {
Packit 5f9837
		/* parent */
Packit 5f9837
		pid = wait(&rc);
Packit 5f9837
		if (!WIFEXITED(rc)) {
Packit 5f9837
			fprintf(stderr, "Child process terminated abnormally.\n");
Packit 5f9837
			rc = EX_SYSERR;
Packit 5f9837
			goto mount_exit;
Packit 5f9837
		}
Packit 5f9837
		rc = WEXITSTATUS(rc);
Packit 5f9837
		if (rc)
Packit 5f9837
			goto mount_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	options = calloc(options_size, 1);
Packit 5f9837
	if (!options) {
Packit 5f9837
		fprintf(stderr, "Unable to allocate memory.\n");
Packit 5f9837
		rc = EX_SYSERR;
Packit 5f9837
		goto mount_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	currentaddress = parsed_info->addrlist;
Packit 5f9837
	nextaddress = strchr(currentaddress, ',');
Packit 5f9837
	if (nextaddress)
Packit 5f9837
		*nextaddress++ = '\0';
Packit 5f9837
Packit 5f9837
mount_retry:
Packit 5f9837
	if (!currentaddress) {
Packit 5f9837
		fprintf(stderr, "Unable to find suitable address.\n");
Packit 5f9837
		rc = parsed_info->nofail ? 0 : EX_FAIL;
Packit 5f9837
		goto mount_exit;
Packit 5f9837
	}
Packit 5f9837
	strlcpy(options, "ip=", options_size);
Packit 5f9837
	strlcat(options, currentaddress, options_size);
Packit 5f9837
Packit 5f9837
	strlcat(options, ",unc=\\\\", options_size);
Packit 5f9837
	strlcat(options, parsed_info->host, options_size);
Packit 5f9837
	strlcat(options, "\\", options_size);
Packit 5f9837
	strlcat(options, parsed_info->share, options_size);
Packit 5f9837
Packit 5f9837
	if (*parsed_info->options) {
Packit 5f9837
		strlcat(options, ",", options_size);
Packit 5f9837
		strlcat(options, parsed_info->options, options_size);
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	if (*parsed_info->prefix) {
Packit 5f9837
		strlcat(options, ",prefixpath=", options_size);
Packit 5f9837
		strlcat(options, parsed_info->prefix, options_size);
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	if (sloppy)
Packit 5f9837
		strlcat(options, ",sloppy", options_size);
Packit 5f9837
Packit 5f9837
	if (parsed_info->verboseflag)
Packit 5f9837
		fprintf(stderr, "%s kernel mount options: %s",
Packit 5f9837
			thisprogram, options);
Packit 5f9837
Packit 5f9837
	if (parsed_info->got_password) {
Packit 5f9837
		/*
Packit 5f9837
		 * Commas have to be doubled, or else they will
Packit 5f9837
		 * look like the parameter separator
Packit 5f9837
		 */
Packit 5f9837
		strlcat(options, ",pass=", options_size);
Packit 5f9837
		strlcat(options, parsed_info->password, options_size);
Packit 5f9837
		if (parsed_info->verboseflag)
Packit 5f9837
			fprintf(stderr, ",pass=********");
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
	if (parsed_info->verboseflag)
Packit 5f9837
		fprintf(stderr, "\n");
Packit 5f9837
Packit 5f9837
	rc = check_mtab(thisprogram, orig_dev, mountpoint);
Packit 5f9837
	if (rc)
Packit 5f9837
		goto mount_exit;
Packit 5f9837
Packit 5f9837
	if (!parsed_info->fakemnt) {
Packit 5f9837
		toggle_dac_capability(0, 1);
Packit 5f9837
		rc = mount(orig_dev, ".", cifs_fstype, parsed_info->flags, options);
Packit 5f9837
		toggle_dac_capability(0, 0);
Packit 5f9837
		if (rc == 0)
Packit 5f9837
			goto do_mtab;
Packit 5f9837
Packit 5f9837
		switch (errno) {
Packit 5f9837
		case ECONNREFUSED:
Packit 5f9837
		case EHOSTUNREACH:
Packit c82b30
			if (currentaddress) {
Packit c82b30
				fprintf(stderr, "mount error(%d): could not connect to %s",
Packit c82b30
					errno, currentaddress);
Packit c82b30
			}
Packit 5f9837
			currentaddress = nextaddress;
Packit 5f9837
			if (currentaddress) {
Packit 5f9837
				nextaddress = strchr(currentaddress, ',');
Packit 5f9837
				if (nextaddress)
Packit 5f9837
					*nextaddress++ = '\0';
Packit 5f9837
			}
Packit 5f9837
			goto mount_retry;
Packit 5f9837
		case ENODEV:
Packit 5f9837
			fprintf(stderr,
Packit 5f9837
				"mount error: %s filesystem not supported by the system\n", cifs_fstype);
Packit 5f9837
			break;
Packit c82b30
		case EHOSTDOWN:
Packit c82b30
			fprintf(stderr,
Packit c82b30
				"mount error: Server abruptly closed the connection.\n"
Packit c82b30
				"This can happen if the server does not support the SMB version you are trying to use.\n"
Packit c82b30
				"The default SMB version recently changed from SMB1 to SMB2.1 and above. Try mounting with vers=1.0.\n");
Packit c82b30
			break;
Packit 5f9837
		case ENXIO:
Packit 5f9837
			if (!already_uppercased &&
Packit 5f9837
			    uppercase_string(parsed_info->host) &&
Packit 5f9837
			    uppercase_string(parsed_info->share) &&
Packit 5f9837
			    uppercase_string(parsed_info->prefix) &&
Packit 5f9837
			    uppercase_string(orig_dev)) {
Packit 5f9837
				fprintf(stderr,
Packit 5f9837
					"Retrying with upper case share name\n");
Packit 5f9837
				already_uppercased = 1;
Packit 5f9837
				goto mount_retry;
Packit 5f9837
			}
Packit 5f9837
		}
Packit 5f9837
		fprintf(stderr, "mount error(%d): %s\n", errno,
Packit 5f9837
			strerror(errno));
Packit 5f9837
		fprintf(stderr,
Packit 5f9837
			"Refer to the %s(8) manual page (e.g. man "
Packit c82b30
			"%s) and kernel log messages (dmesg)\n", thisprogram, thisprogram);
Packit 5f9837
		rc = EX_FAIL;
Packit 5f9837
		goto mount_exit;
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
do_mtab:
Packit 5f9837
	if (!parsed_info->nomtab && !mtab_unusable()) {
Packit 5f9837
		if (parsed_info->flags & MS_REMOUNT) {
Packit 5f9837
			rc = del_mtab(mountpoint);
Packit 5f9837
			if (rc)
Packit 5f9837
				goto mount_exit;
Packit 5f9837
		}
Packit 5f9837
Packit 5f9837
		rc = add_mtab(orig_dev, mountpoint, parsed_info->flags, cifs_fstype);
Packit 5f9837
	}
Packit 5f9837
Packit 5f9837
mount_exit:
Packit 5f9837
	if (parsed_info) {
Packit 5f9837
		memset(parsed_info->password, 0, sizeof(parsed_info->password));
Packit 5f9837
		munmap(parsed_info, sizeof(*parsed_info));
Packit 5f9837
	}
Packit 5f9837
	free(options);
Packit 5f9837
	free(orgoptions);
Packit 5f9837
	return rc;
Packit 5f9837
}