linux-cifs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Shyam Prasad N <nspmangalore@gmail.com>
To: CIFS <linux-cifs@vger.kernel.org>,
	samba-technical@lists.samba.org,
	Pavel Shilovsky <piastryyy@gmail.com>,
	Steve French <smfrench@gmail.com>,
	sribhat.msa@outlook.com
Subject: [PATCH][SMB3] mount.cifs integration with PAM
Date: Fri, 14 Aug 2020 11:15:40 +0530	[thread overview]
Message-ID: <CANT5p=pxPsBwAv3oJX6Ae9wjpZoEjLvyfGM1sM9DEhS11RNgog@mail.gmail.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 1492 bytes --]

Hi,

Currently, for sec=krb5, mount.cifs assumes that the kerberos TGT is
already downloaded and stored in krb5 cred cache file. If an AD user
is logged in through ssh or su, those utilities authenticate with PAM
(winbind or sssd), and winbind/sssd can be configured to perform
krbtgt house-keeping (like refreshing the tickets). However, if the AD
user is not logged in, and the local root user wants to mount the
share using the credentials for an AD user, he/she will need to resort
to manual kinit, and this does not go through winbind/sssd.

Attached patch will introduce PAM authentication in mount.cifs. If
sec=krb5 is specified, mount.cifs will attempt to authenticate with
PAM as the username mentioned in mount options. If the authentication
fails, we fall back to the old behavior and proceed with the mount
nevertheless.

@linux-cifs: Please review the overall flow, and let me know if there
are any issues/suggestions. The feature is enabled by default in a
configure parameter (krb5pam), and can be disabled. Do we also need a
new mount option to trigger this new behavior? (try-pam-auth?)

@samba-technical: Please review the overall flow of PAM
authentication. Currently, I'm mainly doing pam_authenticate and
pam_setcreds. Is there any added benefit opening and closing session?
Is it possible to call pam_open_session from mount.cifs, and then call
pam_close_session in another binary (umount.cifs)?

Also attached the output of my test runs.

Thanks in advance.
-- 
-Shyam

[-- Attachment #2: test-runs.txt --]
[-- Type: text/plain, Size: 1543 bytes --]

localadmin@linux-vm:~$ sudo mount -t cifs //mystorageaccount.file.core.windows.net/share2 /mnt/abc/ -o vers=3.0,sec=krb5,serverino,cifsacl,mfsymlinks,actimeo=60,multiuser,cruid=aduser,username=aduser,domain=mydomain
Authenticating as user: aduser
Password:  (no echo)
localadmin@linux-vm:~$ mount -t cifs
//mystorageaccount.file.core.windows.net/share2 on /mnt/abc type cifs (rw,relatime,vers=3.0,sec=krb5,cruid=11195,cache=strict,multiuser,domain=mydomain,uid=0,noforceuid,gid=0,noforcegid,file_mode=0755,dir_mode=0755,soft,persistenthandles,nounix,serverino,mapposix,cifsacl,mfsymlinks,noperm,rsize=1048576,wsize=1048576,bsize=1048576,echo_interval=60,actimeo=60)

aduser@linux-vm:~$ ls /mnt/abc/
a.sh  abc  bac  datefile  dir1  hahaha  test.sh  testfile

localadmin@linux-vm:~$ sudo mount -t cifs //mystorageaccount.file.core.windows.net/share2 /mnt/abc/ -o vers=3.0,sec=krb5,serverino,cifsacl,mfsymlinks,actimeo=60,multiuser,cruid=aduser,credentials=/home/localadmin/.smb/aduser.creds
Authenticating as user: aduser
localadmin@linux-vm:~$ mount -t cifs
//mystorageaccount.file.core.windows.net/share2 on /mnt/abc type cifs (rw,relatime,vers=3.0,sec=krb5,cruid=11195,cache=strict,multiuser,domain=mydomain,uid=0,noforceuid,gid=0,noforcegid,file_mode=0755,dir_mode=0755,soft,persistenthandles,nounix,serverino,mapposix,cifsacl,mfsymlinks,noperm,rsize=1048576,wsize=1048576,bsize=1048576,echo_interval=60,actimeo=60)
localadmin@linux-vm:~$ ls -l /tmp/krb5cc_11195
-rw------- 1 aduser root 6077 Aug 14 04:19 /tmp/krb5cc_11195


[-- Attachment #3: 0001-mount.cifs-Try-to-authenticate-the-krb5-user-against.patch --]
[-- Type: application/octet-stream, Size: 8807 bytes --]

From cdc897ef5b5d68290cd458c5704da2e3d10d1a62 Mon Sep 17 00:00:00 2001
From: Shyam Prasad N <sprasad@microsoft.com>
Date: Thu, 13 Aug 2020 08:53:08 -0700
Subject: [PATCH] mount.cifs: Try to authenticate the krb5 user against PAM
 before proceeding to call the mount system call. If the authentication fails,
 proceed to try mount, so that if the user has authenticated manually with the
 KDC, the user will still be able to mount.

Authentication against PAM has two benefits:
- The PAM module (winbind or sssd) will perform the house-keeping of
the krb5 tickets and make sure they aren't expired.
- The mount.cifs utility need not rely on sshd or su to authenticate
against PAM and arrange the krb5 TGT.

This introduces a new PAM application named "cifs".
Also, to disable this feature at the time of build, one can run
configure with --enable-krb5pam=no.
---
 Makefile.am  |   2 +-
 configure.ac |  27 ++++++++++
 mount.cifs.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 172 insertions(+), 3 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index fe9cd34..51c5c47 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,7 +4,7 @@ ACLOCAL_AMFLAGS = -I aclocal
 root_sbindir = $(ROOTSBINDIR)
 root_sbin_PROGRAMS = mount.cifs
 mount_cifs_SOURCES = mount.cifs.c mtab.c resolve_host.c util.c
-mount_cifs_LDADD = $(LIBCAP) $(CAPNG_LDADD) $(RT_LDADD)
+mount_cifs_LDADD = $(LIBCAP) $(LIBPAM) $(CAPNG_LDADD) $(RT_LDADD)
 include_HEADERS = cifsidmap.h
 rst_man_pages = mount.cifs.8
 
diff --git a/configure.ac b/configure.ac
index 678b55d..80ed593 100644
--- a/configure.ac
+++ b/configure.ac
@@ -55,6 +55,11 @@ AC_ARG_ENABLE(pam,
 	enable_pam=$enableval,
 	enable_pam="maybe")
 
+AC_ARG_ENABLE(krb5pam,
+    [AS_HELP_STRING([--enable-krb5pam],[Add PAM authentication support when using sec=krb5 @<:@default=yes@])],,
+    enable_krb5pam=$enableval
+    enable_krb5pam="yes")
+
 AC_ARG_ENABLE(systemd,
 	[AS_HELP_STRING([--enable-systemd],[Enable systemd specific behavior for mount.cifs @<:@default=yes@:>@])],
 	enable_systemd=$enableval,
@@ -241,6 +246,27 @@ if test $enable_pam != "no"; then
 			])
 fi
 
+if test "x$enable_krb5pam" = "xno"; then
+	enable_krb5pam="no"
+else
+	AC_CHECK_LIB([pam], [pam_start], enable_krb5pam="yes", enable_krb5pam="no", )
+	AC_CHECK_HEADERS([security/pam_appl.h], ,
+			 [
+				if test $enable_krb5pam = "yes"; then
+					AC_MSG_ERROR([security/pam_appl.h not found, consider installing keyutils-libs-devel.])
+				else
+					AC_MSG_WARN([security/pam_appl.h not found, consider installing pam-devel. Disabling cifscreds PAM module.])
+					enable_krb5pam="no"
+				fi
+			 ])
+fi
+
+if test "$enable_krb5pam" = "yes"; then
+	AC_DEFINE([HAVE_KRB5PAM],[1], [Define if PAM auth is enabled for krb5])
+	LIBPAM=-lpam
+	AC_SUBST(LIBPAM)
+fi
+
 # ugly, but I'm not sure how to check for functions in a library that's not in $LIBS
 cu_saved_libs=$LIBS
 LIBS="$LIBS $KRB5_LDADD"
@@ -288,6 +314,7 @@ AM_CONDITIONAL(CONFIG_CIFSACL, [test "$enable_cifsacl" != "no"])
 AM_CONDITIONAL(CONFIG_SMBINFO, [test "$enable_smbinfo" != "no"])
 AM_CONDITIONAL(CONFIG_PYTHON_TOOLS, [test "$enable_pythontools" != "no"])
 AM_CONDITIONAL(CONFIG_PAM, [test "$enable_pam" != "no"])
+AM_CONDITIONAL(CONFIG_KRB5PAM, [test "$enable_krb5pam" != "no"])
 AM_CONDITIONAL(CONFIG_PLUGIN, [test "$enable_cifsidmap" != "no" -o "$enable_cifsacl" != "no"])
 
 LIBCAP_NG_PATH
diff --git a/mount.cifs.c b/mount.cifs.c
index 40918c1..fe67e3f 100644
--- a/mount.cifs.c
+++ b/mount.cifs.c
@@ -46,6 +46,9 @@
 #include <time.h>
 #include <sys/mman.h>
 #include <sys/wait.h>
+#ifdef HAVE_KRB5PAM
+#include <security/pam_appl.h>
+#endif /* HAVE_KRB5PAM */
 #ifdef HAVE_SYS_FSUID_H
 #include <sys/fsuid.h>
 #endif /* HAVE_SYS_FSUID_H */
@@ -189,6 +192,7 @@ struct parsed_mount_info {
 	unsigned int verboseflag:1;
 	unsigned int nofail:1;
 	unsigned int got_domain:1;
+	unsigned int is_krb5:1;
 };
 
 static const char *thisprogram;
@@ -891,9 +895,10 @@ parse_options(const char *data, struct parsed_mount_info *parsed_info)
 
 		case OPT_SEC:
 			if (value) {
-				if (!strncmp(value, "none", 4) ||
-				    !strncmp(value, "krb5", 4))
+				if (!strncmp(value, "none", 4))
 					parsed_info->got_password = 1;
+				if (!strncmp(value, "krb5", 4))
+					parsed_info->is_krb5 = 1;
 			}
 			break;
 
@@ -1755,6 +1760,121 @@ get_password(const char *prompt, char *input, int capacity)
 	return input;
 }
 
+#ifdef HAVE_KRB5PAM
+#define PAM_CIFS_SERVICE "cifs"
+
+static int
+pam_auth_krb5_conv(int n, const struct pam_message **msg,
+    struct pam_response **resp, void *data)
+{
+    struct parsed_mount_info *parsed_info;
+	struct pam_response *reply;
+	int i;
+
+	*resp = NULL;
+
+    parsed_info = data;
+    if (parsed_info == NULL)
+		return (PAM_CONV_ERR);
+
+	if (n <= 0 || n > PAM_MAX_NUM_MSG)
+		return (PAM_CONV_ERR);
+
+	if ((reply = calloc(n, sizeof(*reply))) == NULL)
+		return (PAM_CONV_ERR);
+
+	for (i = 0; i < n; ++i) {
+		switch (msg[i]->msg_style) {
+		case PAM_PROMPT_ECHO_OFF:
+            if ((reply[i].resp = (char *) malloc(MOUNT_PASSWD_SIZE + 1)) == NULL)
+                goto fail;
+
+            if (parsed_info->got_password && parsed_info->password != NULL) {
+                strncpy(reply[i].resp, parsed_info->password, MOUNT_PASSWD_SIZE + 1);
+            } else if (get_password(msg[i]->msg, reply[i].resp, MOUNT_PASSWD_SIZE + 1) == NULL) {
+                goto fail;
+            }
+            reply[i].resp[MOUNT_PASSWD_SIZE] = '\0';
+
+			reply[i].resp_retcode = PAM_SUCCESS;
+			break;
+		case PAM_PROMPT_ECHO_ON:
+			fprintf(stderr, "%s: ", msg[i]->msg);
+            if ((reply[i].resp = (char *) malloc(MOUNT_PASSWD_SIZE + 1)) == NULL)
+                goto fail;
+
+            if (parsed_info->got_password && parsed_info->password != NULL) {
+                strncpy(reply[i].resp, parsed_info->password, MOUNT_PASSWD_SIZE + 1);
+            } else if (fgets(reply[i].resp, MOUNT_PASSWD_SIZE + 1, stdin) == NULL) {
+                goto fail;
+            }
+            reply[i].resp[MOUNT_PASSWD_SIZE] = '\0';
+
+			reply[i].resp_retcode = PAM_SUCCESS;
+			break;
+		case PAM_ERROR_MSG:
+		case PAM_TEXT_INFO:
+			fprintf(stderr, "%s: ", msg[i]->msg);
+
+			reply[i].resp_retcode = PAM_SUCCESS;
+			break;
+		default:
+			goto fail;
+		}
+	}
+	*resp = reply;
+	return (PAM_SUCCESS);
+
+ fail:
+	for(i = 0; i < n; i++) {
+        if (reply[i].resp)
+            free(reply[i].resp);
+	}
+	free(reply);
+	return (PAM_CONV_ERR);
+}
+
+static int 
+pam_auth_krb5_user(struct parsed_mount_info *parsed_info)
+{
+    int rc = -1;
+    pam_handle_t *pamh = NULL;
+    struct pam_conv pam_conv = {
+        .conv = pam_auth_krb5_conv,
+        .appdata_ptr = (void *) parsed_info
+    };
+    
+    fprintf(stdout, "Authenticating as user: %s\n", parsed_info->username);
+    rc = pam_start(PAM_CIFS_SERVICE, parsed_info->username, &pam_conv, &pamh);
+    if (rc != PAM_SUCCESS) {
+        fprintf(stderr, "Error starting PAM transaction: %s\n", pam_strerror(pamh, rc));
+        return rc;
+    }
+
+    rc = pam_authenticate(pamh, 0);
+    if (rc != PAM_SUCCESS) {
+        fprintf(stderr, "Error in authenticating user with PAM: %s\n", pam_strerror(pamh, rc));
+        goto end;
+    }
+
+    rc = pam_acct_mgmt(pamh, 0);
+    if (rc != PAM_SUCCESS) {
+        fprintf(stderr, "User account invalid: %s\n", pam_strerror(pamh, rc));
+        goto end;
+    }
+
+    rc = pam_setcred(pamh, PAM_ESTABLISH_CRED);
+    if (rc != PAM_SUCCESS) {
+        fprintf(stderr, "Error in setting PAM credentials: %s\n", pam_strerror(pamh, rc));
+        goto end;
+    }
+
+end:
+    pam_end(pamh, rc);
+    return rc;
+}
+#endif /* HAVE_KRB5PAM */
+
 static int
 assemble_mountinfo(struct parsed_mount_info *parsed_info,
 		   const char *thisprogram, const char *mountpoint,
@@ -1837,6 +1957,28 @@ assemble_mountinfo(struct parsed_mount_info *parsed_info,
 		parsed_info->got_user = 1;
 	}
 
+#ifdef HAVE_KRB5PAM
+	if (parsed_info->is_krb5) {
+		/* 
+		 * Attempt to authenticate with PAM. 
+		 * If PAM is configured properly, let it get the krb5 tickets necessary for the mount.
+		 * Even if this fails, it could be the case of PAM not configured properly. 
+		 * In that case, retain the current behavior. So this is just a best-effort.
+		 */
+		rc = pam_auth_krb5_user(parsed_info);
+		if (rc) {
+			fprintf(stderr, "Attempt to authenticate user with " \
+					"PAM unsuccessful. Still, proceeding with mount.\n");
+            /*
+             * Even if this is a failure, fallthrough and see if cifs.ko can still 
+             * authenticate the user.
+             */
+		}
+
+		parsed_info->got_password = 1;
+	}
+#endif /* HAVE_KRB5PAM */
+
 	if (!parsed_info->got_password) {
 		char tmp_pass[MOUNT_PASSWD_SIZE + 1];
 		char *prompt = NULL;
-- 
2.25.1


             reply	other threads:[~2020-08-14  5:45 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-08-14  5:45 Shyam Prasad N [this message]
2020-08-14  9:52 ` [PATCH][SMB3] mount.cifs integration with PAM Aurélien Aptel
     [not found]   ` <CANT5p=oeY91u17DPe6WO75Eq_bjzrVC0kmAErrZ=h3S1qh-Wxw@mail.gmail.com>
2020-08-17  8:48     ` Aurélien Aptel
     [not found]       ` <CANT5p=rxp3iQMgxaM_mn3RE3B+zezWr3o8zpkFyWUR27CpeVCA@mail.gmail.com>
2020-09-09 11:04         ` Shyam Prasad N
2020-09-09 14:13           ` Aurélien Aptel
2020-09-09 17:25             ` Shyam Prasad N
2020-09-10  9:43               ` Aurélien Aptel
2020-09-23 12:06                 ` Shyam Prasad N
2020-09-23 13:56                   ` Aurélien Aptel
2020-09-24 10:39                     ` Shyam Prasad N
2020-11-09 23:42                       ` Pavel Shilovsky
2020-11-10 13:20                         ` Shyam Prasad N
2020-11-10 19:22                           ` Pavel Shilovsky
2020-11-27 10:43                             ` Shyam Prasad N
2020-12-14 18:03                               ` Stefan Metzmacher
     [not found]                                 ` <CANT5p=rYiY0xE-35swsFKVitZD2yTchRiReyA0wVvY+mU_qKEw@mail.gmail.com>
2021-01-30 14:24                                   ` Shyam Prasad N
2021-02-01 10:51                                     ` Aurélien Aptel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CANT5p=pxPsBwAv3oJX6Ae9wjpZoEjLvyfGM1sM9DEhS11RNgog@mail.gmail.com' \
    --to=nspmangalore@gmail.com \
    --cc=linux-cifs@vger.kernel.org \
    --cc=piastryyy@gmail.com \
    --cc=samba-technical@lists.samba.org \
    --cc=smfrench@gmail.com \
    --cc=sribhat.msa@outlook.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).