All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH][SMB3] mount.cifs integration with PAM
@ 2020-08-14  5:45 Shyam Prasad N
  2020-08-14  9:52 ` Aurélien Aptel
  0 siblings, 1 reply; 17+ messages in thread
From: Shyam Prasad N @ 2020-08-14  5:45 UTC (permalink / raw)
  To: CIFS, samba-technical, Pavel Shilovsky, Steve French, sribhat.msa

[-- 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


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH][SMB3] mount.cifs integration with PAM
  2020-08-14  5:45 [PATCH][SMB3] mount.cifs integration with PAM Shyam Prasad N
@ 2020-08-14  9:52 ` Aurélien Aptel
       [not found]   ` <CANT5p=oeY91u17DPe6WO75Eq_bjzrVC0kmAErrZ=h3S1qh-Wxw@mail.gmail.com>
  0 siblings, 1 reply; 17+ messages in thread
From: Aurélien Aptel @ 2020-08-14  9:52 UTC (permalink / raw)
  To: Shyam Prasad N, CIFS, samba-technical, Pavel Shilovsky,
	Steve French, sribhat.msa

Hi Shyam,

Shyam Prasad N <nspmangalore@gmail.com> writes:
> 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.

That is correct, I think. Note that using when login in the system PAM
also sets up KRB5CCNAME variable that points to the credential cache
(e.g. "FILE:/tmp/krb5cc_0") and is then inherited in all processes in
the session.

> 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.

Shouldn't we do it the other way around? i.e. try to use any existing
credential cache, and if that fails auth again with PAM. I think we
might end up overwriting an existing cache or logging in twice
otherwise.

> @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)?

I am not 100% sure about this but I think the session should be opened
in the context of the parent shell process to be able to be persistent,
otherwise the session will close when mount.cifs exits. Maybe there is a
way to pin the session on a different processes... But most likely there
is an existing session opened by PAM when the user initially logged in
the system (regardless of the PAM backend/params).

Cheers,
-- 
Aurélien Aptel / SUSE Labs Samba Team
GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH][SMB3] mount.cifs integration with PAM
       [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>
  0 siblings, 1 reply; 17+ messages in thread
From: Aurélien Aptel @ 2020-08-17  8:48 UTC (permalink / raw)
  To: Shyam Prasad N
  Cc: CIFS, samba-technical, Pavel Shilovsky, Steve French, sribhat.msa

Shyam Prasad N <nspmangalore@gmail.com> writes:
> Agreed. But since we're not dealing with krb5cc file directly in
> mount.cifs, I don't see it influencing this change. However, I will test it
> out too.

When reconnecting or accessing DFS links (cross-server symlinks) the
client opens a new connection to the target server and has to auth
again. Since there are no ways to ask for a password at that moment
(we're in the middle of some syscall) cifs.ko does an upcall to
cifs.upcall and passes the pid of the process who initiated the
syscall. cifs.upcall then reads that proc env (via /proc/<pid>/environ)
and looks for KRB5CCNAME, uses it and returns the required data for
cifs.ko to proceed with the SMB Session Setup.

So it is important to have this env var set if the location of the
credential cache is not the default one. If you do PAM login from
mount.cifs, the env var might be set for that process but it will only
persist in children processes of mount.cifs i.e. most likely none.

I still think this patch is a good idea but we should definitely print
something to the user that things might fail later on, or give
instructions to set the env var in the user shell or something like that.

> That does make sense. I was thinking of including a mount option to enable
> this path. But let me explore the retry-on-failure path as well.

Mount option sounds good regardless.

> Yeah. I didn't get the complete picture on session maintenance after
> reading the pam application developer's guide.
> Was hoping that somebody on samba-technical would have some idea about this.

The keyring docs have some info on it too but it's still not clear to
me.

https://man7.org/linux/man-pages/man7/session-keyring.7.html

Cheers,
-- 
Aurélien Aptel / SUSE Labs Samba Team
GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH][SMB3] mount.cifs integration with PAM
       [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
  0 siblings, 1 reply; 17+ messages in thread
From: Shyam Prasad N @ 2020-09-09 11:04 UTC (permalink / raw)
  To: Aurélien Aptel
  Cc: CIFS, samba-technical, Pavel Shilovsky, Steve French, sribhat.msa

I did some code reading on samba-winbind (and sssd to some extent),
and here are a few things I noticed.
1. Both today have almost "no-op" handlers for open and close of PAM sessions.
2. However, the login and logoff do have some functionalities. PAM
setcred is done both on log-on and log-off, so that the cred cache is
deleted on the last logoff.
3. Login and logoff have ref counts associated with a user. But I do
notice that a kinit is done on every login (even if the user is
already logged on).

So it looks like (at least as of today) we don't break much by
authenticating with PAM. One additional change needed would be to
introduce umount.cifs, which deletes PAM credentials for the user.

Alternatively, another option is to not rely on winbind/sssd for
authentication (or maintaining krb5 tgt up-to-date), but instead let
cifs-utils deal with it separately. Today, cifs.upcall has a way to
kinit if the cred cache file is missing. But that needs the keytab
file to be populated with the key for the user in question. We could
also try a kinit based on password in mount.cifs (if the cred cache is
missing), and if that works, populate the keytab file with the key for
this user (for cifs.upcall to use later, when necessary).

Thoughts?

Regards,
Shyam

On Mon, Aug 17, 2020 at 2:42 PM Shyam Prasad N <nspmangalore@gmail.com> wrote:
>
> Thanks Aurélien. Good points.
> Let me take a closer look at this and see how to proceed.
>
> Regards,
> Shyam
>
> On Mon, Aug 17, 2020, 14:18 Aurélien Aptel <aaptel@suse.com> wrote:
>>
>> Shyam Prasad N <nspmangalore@gmail.com> writes:
>> > Agreed. But since we're not dealing with krb5cc file directly in
>> > mount.cifs, I don't see it influencing this change. However, I will test it
>> > out too.
>>
>> When reconnecting or accessing DFS links (cross-server symlinks) the
>> client opens a new connection to the target server and has to auth
>> again. Since there are no ways to ask for a password at that moment
>> (we're in the middle of some syscall) cifs.ko does an upcall to
>> cifs.upcall and passes the pid of the process who initiated the
>> syscall. cifs.upcall then reads that proc env (via /proc/<pid>/environ)
>> and looks for KRB5CCNAME, uses it and returns the required data for
>> cifs.ko to proceed with the SMB Session Setup.
>>
>> So it is important to have this env var set if the location of the
>> credential cache is not the default one. If you do PAM login from
>> mount.cifs, the env var might be set for that process but it will only
>> persist in children processes of mount.cifs i.e. most likely none.
>>
>> I still think this patch is a good idea but we should definitely print
>> something to the user that things might fail later on, or give
>> instructions to set the env var in the user shell or something like that.
>>
>> > That does make sense. I was thinking of including a mount option to enable
>> > this path. But let me explore the retry-on-failure path as well.
>>
>> Mount option sounds good regardless.
>>
>> > Yeah. I didn't get the complete picture on session maintenance after
>> > reading the pam application developer's guide.
>> > Was hoping that somebody on samba-technical would have some idea about this.
>>
>> The keyring docs have some info on it too but it's still not clear to
>> me.
>>
>> https://man7.org/linux/man-pages/man7/session-keyring.7.html
>>
>> Cheers,
>> --
>> Aurélien Aptel / SUSE Labs Samba Team
>> GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
>> SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
>> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)



-- 
-Shyam

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH][SMB3] mount.cifs integration with PAM
  2020-09-09 11:04         ` Shyam Prasad N
@ 2020-09-09 14:13           ` Aurélien Aptel
  2020-09-09 17:25             ` Shyam Prasad N
  0 siblings, 1 reply; 17+ messages in thread
From: Aurélien Aptel @ 2020-09-09 14:13 UTC (permalink / raw)
  To: Shyam Prasad N
  Cc: CIFS, samba-technical, Pavel Shilovsky, Steve French, sribhat.msa

Shyam Prasad N <nspmangalore@gmail.com> writes:
> Thoughts?

You are reaching the limits of my poor understanding of this kerberos
stuff. What is the difference between keytab and credential cache?

So IIUC you are proposing 2 ways to go about it:

a) - do PAM login in mount.cifs (which in turns calls into sssd/winbind)
   - implement umount.cifs for PAM logoff
   
b) - ignore PAM and winbind/sssd and do kinit in mount.cifs manually
   - would this requires umount.cifs as well?

I like (b) because it feels we have more control and don't require a big
external program like winbind *but* if (b) doesn't do the refreshing of
the tickets then the mount will always stop working after they
expire. This seems only useful for quick one-off mounting or
testing/debugging. Real end users will find it unreliable unless they
setup something like what winbind does essentially.

So ultimately, to me, (a) seems like the better choice. Let me know if I
misunderstood something.

Cheers,
-- 
Aurélien Aptel / SUSE Labs Samba Team
GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH][SMB3] mount.cifs integration with PAM
  2020-09-09 14:13           ` Aurélien Aptel
@ 2020-09-09 17:25             ` Shyam Prasad N
  2020-09-10  9:43               ` Aurélien Aptel
  0 siblings, 1 reply; 17+ messages in thread
From: Shyam Prasad N @ 2020-09-09 17:25 UTC (permalink / raw)
  To: Aurélien Aptel
  Cc: CIFS, samba-technical, Pavel Shilovsky, Steve French, sribhat.msa

Your understanding is correct. We could also go for a hybrid approach,
where we fallback to option b when option a fails to authenticate.
But for now, I'll resubmit a patch with option a as a fallback when
regular mount fails, just like you had suggested.

Regards,
Shyam

On Wed, Sep 9, 2020 at 7:43 PM Aurélien Aptel <aaptel@suse.com> wrote:
>
> Shyam Prasad N <nspmangalore@gmail.com> writes:
> > Thoughts?
>
> You are reaching the limits of my poor understanding of this kerberos
> stuff. What is the difference between keytab and credential cache?
>
> So IIUC you are proposing 2 ways to go about it:
>
> a) - do PAM login in mount.cifs (which in turns calls into sssd/winbind)
>    - implement umount.cifs for PAM logoff
>
> b) - ignore PAM and winbind/sssd and do kinit in mount.cifs manually
>    - would this requires umount.cifs as well?
>
> I like (b) because it feels we have more control and don't require a big
> external program like winbind *but* if (b) doesn't do the refreshing of
> the tickets then the mount will always stop working after they
> expire. This seems only useful for quick one-off mounting or
> testing/debugging. Real end users will find it unreliable unless they
> setup something like what winbind does essentially.
>
> So ultimately, to me, (a) seems like the better choice. Let me know if I
> misunderstood something.
>
> Cheers,
> --
> Aurélien Aptel / SUSE Labs Samba Team
> GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
> SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)



-- 
-Shyam

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH][SMB3] mount.cifs integration with PAM
  2020-09-09 17:25             ` Shyam Prasad N
@ 2020-09-10  9:43               ` Aurélien Aptel
  2020-09-23 12:06                 ` Shyam Prasad N
  0 siblings, 1 reply; 17+ messages in thread
From: Aurélien Aptel @ 2020-09-10  9:43 UTC (permalink / raw)
  To: Shyam Prasad N
  Cc: CIFS, samba-technical, Pavel Shilovsky, Steve French, sribhat.msa

Shyam Prasad N <nspmangalore@gmail.com> writes:
> Your understanding is correct. We could also go for a hybrid approach,
> where we fallback to option b when option a fails to authenticate.
> But for now, I'll resubmit a patch with option a as a fallback when
> regular mount fails, just like you had suggested.

Please try DFS setups as well. On DFS links a sub-mount is made from the
kernel and mount.cifs is not called.

Cheers,
-- 
Aurélien Aptel / SUSE Labs Samba Team
GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH][SMB3] mount.cifs integration with PAM
  2020-09-10  9:43               ` Aurélien Aptel
@ 2020-09-23 12:06                 ` Shyam Prasad N
  2020-09-23 13:56                   ` Aurélien Aptel
  0 siblings, 1 reply; 17+ messages in thread
From: Shyam Prasad N @ 2020-09-23 12:06 UTC (permalink / raw)
  To: Aurélien Aptel
  Cc: CIFS, samba-technical, Pavel Shilovsky, Steve French, sribhat.msa

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

I tried to use this as a fallback option, but there's an issue. During
the first mount attempt, cifs.upcall pushes the credentials lookup
failure to the keyring. So on the next attempt, request_key() doesn't
even call into the userspace, since there's already an entry there. So
we'll probably have to call it under a new child process on the
fallback attempt.

@Aurélien Aptel Do you think that it is worth the effort? or should we
just keep force_pam as a mount option?

Also, I'll test this out with DFS once I figure out how to set it up. :)
Re-attaching the patch with some minor changes with just the
"force_pam" mount option.

Regards,
Shyam

On Thu, Sep 10, 2020 at 3:13 PM Aurélien Aptel <aaptel@suse.com> wrote:
>
> Shyam Prasad N <nspmangalore@gmail.com> writes:
> > Your understanding is correct. We could also go for a hybrid approach,
> > where we fallback to option b when option a fails to authenticate.
> > But for now, I'll resubmit a patch with option a as a fallback when
> > regular mount fails, just like you had suggested.
>
> Please try DFS setups as well. On DFS links a sub-mount is made from the
> kernel and mount.cifs is not called.
>
> Cheers,
> --
> Aurélien Aptel / SUSE Labs Samba Team
> GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
> SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)



-- 
-Shyam

[-- Attachment #2: 0001-mount.cifs-Have-an-option-to-authenticate-against-PA.patch --]
[-- Type: application/octet-stream, Size: 9450 bytes --]

From 6ad829f2e0e3d0390580d1a2a0bf74b7f8a321aa 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: Have an option to authenticate against PAM.

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. In case of ssh with private keys,
ssh doesn't involve PAM. So krb5 TGT may be missing.

This introduces a new PAM application named "cifs".
The user may use this to customize the PAM stack for just this application.
Otherwise, the default PAM configuration is used.
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 | 157 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 182 insertions(+), 4 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 22e78ef..fa8af0f 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 4feb397..4a0d03f 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 */
@@ -163,6 +166,7 @@
 #define OPT_BKUPGID    31
 #define OPT_NOFAIL     32
 #define OPT_SNAPSHOT   33
+#define OPT_FORCE_PAM  34
 
 #define MNT_TMP_FILE "/.mtab.cifs.XXXXXX"
 
@@ -189,6 +193,8 @@ struct parsed_mount_info {
 	unsigned int verboseflag:1;
 	unsigned int nofail:1;
 	unsigned int got_domain:1;
+	unsigned int is_krb5:1;
+	unsigned int force_pam:1;
 };
 
 static const char *thisprogram;
@@ -713,6 +719,8 @@ static int parse_opt_token(const char *token)
 		return OPT_PASS;
 	if (strcmp(token, "sec") == 0)
 		return OPT_SEC;
+	if (strcmp(token, "force_pam") == 0)
+		return OPT_FORCE_PAM;
 	if (strcmp(token, "ip") == 0 ||
 		strcmp(token, "addr") == 0)
 		return OPT_IP;
@@ -891,12 +899,19 @@ 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;
 
+#ifdef HAVE_KRB5PAM
+		case OPT_FORCE_PAM:
+			parsed_info->force_pam = 1;
+			goto nocopy;
+#endif /* HAVE_KRB5PAM */
+			
 		case OPT_IP:
 			if (!value || !*value) {
 				fprintf(stderr,
@@ -1809,6 +1824,119 @@ 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 (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,
@@ -1891,7 +2019,30 @@ assemble_mountinfo(struct parsed_mount_info *parsed_info,
 		parsed_info->got_user = 1;
 	}
 
-	if (!parsed_info->got_password) {
+#ifdef HAVE_KRB5PAM
+	if (parsed_info->is_krb5 && parsed_info->force_pam) {
+		/* 
+		 * 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 sec=krb5, then password is collected by other means */
+	if (!parsed_info->is_krb5 && !parsed_info->got_password) {
 		char tmp_pass[MOUNT_PASSWD_SIZE + 1];
 		char *prompt = NULL;
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH][SMB3] mount.cifs integration with PAM
  2020-09-23 12:06                 ` Shyam Prasad N
@ 2020-09-23 13:56                   ` Aurélien Aptel
  2020-09-24 10:39                     ` Shyam Prasad N
  0 siblings, 1 reply; 17+ messages in thread
From: Aurélien Aptel @ 2020-09-23 13:56 UTC (permalink / raw)
  To: Shyam Prasad N
  Cc: CIFS, samba-technical, Pavel Shilovsky, Steve French, sribhat.msa

Shyam Prasad N <nspmangalore@gmail.com> writes:
> Also, I'll test this out with DFS once I figure out how to set it up. :)
> Re-attaching the patch with some minor changes with just the
> "force_pam" mount option.

You will need 2 Windows VM. DFS is basically symlinks across
servers. The DFS root VM will host the links (standalone namespace) and
you have to make them point to shares on the 2nd VM. You don't need to
setup replication to test.

When you mount the root in cifs.ko and access a link, the server will
reply that the file is remote. cifs.ko then does an FSCTL on the link to
resolve the target it points to and then connects to the target and
mounts it under the link seemlessly.


Regarding the patch:

* need to update the man page with option and explanation

I have some comments with the style, I know it's annoying.. but it
would be best to keep the same across the code.

* use the existing indent style (tabs) and avoid adding trailing whitespaces.
* no () for return statements
* no casting for memory allocation
* if (X) free(X)  => free(X)

Below some comments about pam_auth_krb5_conv():

> @@ -1809,6 +1824,119 @@ 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 (fgets(reply[i].resp, MOUNT_PASSWD_SIZE + 1, stdin) == NULL)

Do we need to remove the trailing \n from the buffer?

> +                goto fail;
> +
> +            reply[i].resp[MOUNT_PASSWD_SIZE] = '\0';
> +
> +			reply[i].resp_retcode = PAM_SUCCESS;
> +			break;
> +		case PAM_ERROR_MSG:

Shouldn't this PAM_ERROR_MSG case goto fail?

> +		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(NULL) is a no-op, remove the checks.

> +	}
> +	free(reply);
> +	return (PAM_CONV_ERR);
> +}

I gave this a try with a properly configured system joined to AD from
local root account:

aaptel$ ./configure
...
checking krb5.h usability... yes
checking krb5.h presence... yes
checking for krb5.h... yes
checking krb5/krb5.h usability... yes
checking krb5/krb5.h presence... yes
checking for krb5/krb5.h... yes
checking for keyvalue in krb5_keyblock... no
...
checking keyutils.h usability... yes
checking keyutils.h presence... yes
checking for keyutils.h... yes
checking for krb5_init_context in -lkrb5... yes
...
checking for WBCLIENT... yes
checking for wbcSidsToUnixIds in -lwbclient... yes
...
checking for keyutils.h... (cached) yes
checking security/pam_appl.h usability... yes
checking security/pam_appl.h presence... yes
checking for security/pam_appl.h... yes
checking for pam_start in -lpam... yes
checking for security/pam_appl.h... (cached) yes
checking for krb5_auth_con_getsendsubkey... yes
checking for krb5_principal_get_realm... no
checking for krb5_free_unparsed_name... yes
checking for krb5_auth_con_setaddrs... yes
checking for krb5_auth_con_set_req_cksumtype... yes
...
aaptel$ make
....(ok)

Without force_pam:

root# ./mount.cifs -v //adnuc.nuc.test/data /x -o sec=krb5,username=administrator,domain=NUC
mount.cifs kernel mount options: ip=192.168.2.111,unc=\\adnuc.nuc.test\data,sec=krb5,user=administrator,domain=NUC
mount error(2): No such file or directory
Refer to the mount.cifs(8) manual page (e.g. man mount.cifs) and kernel log messages (dmesg)

With force_pam:

root# ./mount.cifs -v //adnuc.nuc.test/data /x -o sec=krb5,username=administrator,domain=NUC,force_pam
Authenticating as user: administrator
Error in authenticating user with PAM: Authentication failure
Attempt to authenticate user with PAM unsuccessful. Still, proceeding with mount.

=> no further message but mount failed and no msg in dmesg, it didn't
   reach the mount() call

Not sure what is going on. Does the domain need to be passed to PAM?

Cheers,
-- 
Aurélien Aptel / SUSE Labs Samba Team
GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH][SMB3] mount.cifs integration with PAM
  2020-09-23 13:56                   ` Aurélien Aptel
@ 2020-09-24 10:39                     ` Shyam Prasad N
  2020-11-09 23:42                       ` Pavel Shilovsky
  0 siblings, 1 reply; 17+ messages in thread
From: Shyam Prasad N @ 2020-09-24 10:39 UTC (permalink / raw)
  To: Aurélien Aptel
  Cc: CIFS, samba-technical, Pavel Shilovsky, Steve French, sribhat.msa

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

Hi Aurélien,

I've implemented most of your review comments. Also fixed the issue.

On Wed, Sep 23, 2020 at 7:26 PM Aurélien Aptel <aaptel@suse.com> wrote:
>
> Shyam Prasad N <nspmangalore@gmail.com> writes:
> > Also, I'll test this out with DFS once I figure out how to set it up. :)
> > Re-attaching the patch with some minor changes with just the
> > "force_pam" mount option.
>
> You will need 2 Windows VM. DFS is basically symlinks across
> servers. The DFS root VM will host the links (standalone namespace) and
> you have to make them point to shares on the 2nd VM. You don't need to
> setup replication to test.
>
> When you mount the root in cifs.ko and access a link, the server will
> reply that the file is remote. cifs.ko then does an FSCTL on the link to
> resolve the target it points to and then connects to the target and
> mounts it under the link seemlessly.
>
>
> Regarding the patch:
>
> * need to update the man page with option and explanation
>
> I have some comments with the style, I know it's annoying.. but it
> would be best to keep the same across the code.
>
> * use the existing indent style (tabs) and avoid adding trailing whitespaces.
> * no () for return statements
> * no casting for memory allocation
> * if (X) free(X)  => free(X)
>
> Below some comments about pam_auth_krb5_conv():
>
> > @@ -1809,6 +1824,119 @@ 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 (fgets(reply[i].resp, MOUNT_PASSWD_SIZE + 1, stdin) == NULL)
>
> Do we need to remove the trailing \n from the buffer?
>
> > +                goto fail;
> > +
> > +            reply[i].resp[MOUNT_PASSWD_SIZE] = '\0';
> > +
> > +                     reply[i].resp_retcode = PAM_SUCCESS;
> > +                     break;
> > +             case PAM_ERROR_MSG:
>
> Shouldn't this PAM_ERROR_MSG case goto fail?
>
> > +             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(NULL) is a no-op, remove the checks.
>
> > +     }
> > +     free(reply);
> > +     return (PAM_CONV_ERR);
> > +}
>
> I gave this a try with a properly configured system joined to AD from
> local root account:
>
> aaptel$ ./configure
> ...
> checking krb5.h usability... yes
> checking krb5.h presence... yes
> checking for krb5.h... yes
> checking krb5/krb5.h usability... yes
> checking krb5/krb5.h presence... yes
> checking for krb5/krb5.h... yes
> checking for keyvalue in krb5_keyblock... no
> ...
> checking keyutils.h usability... yes
> checking keyutils.h presence... yes
> checking for keyutils.h... yes
> checking for krb5_init_context in -lkrb5... yes
> ...
> checking for WBCLIENT... yes
> checking for wbcSidsToUnixIds in -lwbclient... yes
> ...
> checking for keyutils.h... (cached) yes
> checking security/pam_appl.h usability... yes
> checking security/pam_appl.h presence... yes
> checking for security/pam_appl.h... yes
> checking for pam_start in -lpam... yes
> checking for security/pam_appl.h... (cached) yes
> checking for krb5_auth_con_getsendsubkey... yes
> checking for krb5_principal_get_realm... no
> checking for krb5_free_unparsed_name... yes
> checking for krb5_auth_con_setaddrs... yes
> checking for krb5_auth_con_set_req_cksumtype... yes
> ...
> aaptel$ make
> ....(ok)
>
> Without force_pam:
>
> root# ./mount.cifs -v //adnuc.nuc.test/data /x -o sec=krb5,username=administrator,domain=NUC
> mount.cifs kernel mount options: ip=192.168.2.111,unc=\\adnuc.nuc.test\data,sec=krb5,user=administrator,domain=NUC
> mount error(2): No such file or directory
> Refer to the mount.cifs(8) manual page (e.g. man mount.cifs) and kernel log messages (dmesg)
>
> With force_pam:
>
> root# ./mount.cifs -v //adnuc.nuc.test/data /x -o sec=krb5,username=administrator,domain=NUC,force_pam
> Authenticating as user: administrator
> Error in authenticating user with PAM: Authentication failure
> Attempt to authenticate user with PAM unsuccessful. Still, proceeding with mount.
>
> => no further message but mount failed and no msg in dmesg, it didn't
>    reach the mount() call
>
> Not sure what is going on. Does the domain need to be passed to PAM?
>
> Cheers,
> --
> Aurélien Aptel / SUSE Labs Samba Team
> GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
> SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)



-- 
-Shyam

[-- Attachment #2: 0001-mount.cifs-Have-an-option-to-authenticate-against-PA.patch --]
[-- Type: application/octet-stream, Size: 9158 bytes --]

From 67fa592800d74a0dc85717c5edeb06bc26c6f476 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: Have an option to authenticate against PAM.

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. In case of ssh with private keys,
ssh doesn't involve PAM. So krb5 TGT may be missing.

This introduces a new PAM application named "cifs".
The user may use this to customize the PAM stack for just this application.
Otherwise, the default PAM configuration is used.
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 | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 183 insertions(+), 4 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 22e78ef..fa8af0f 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 4feb397..271966b 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 */
@@ -163,6 +166,7 @@
 #define OPT_BKUPGID    31
 #define OPT_NOFAIL     32
 #define OPT_SNAPSHOT   33
+#define OPT_FORCE_PAM  34
 
 #define MNT_TMP_FILE "/.mtab.cifs.XXXXXX"
 
@@ -189,6 +193,8 @@ struct parsed_mount_info {
 	unsigned int verboseflag:1;
 	unsigned int nofail:1;
 	unsigned int got_domain:1;
+	unsigned int is_krb5:1;
+	unsigned int force_pam:1;
 };
 
 static const char *thisprogram;
@@ -713,6 +719,8 @@ static int parse_opt_token(const char *token)
 		return OPT_PASS;
 	if (strcmp(token, "sec") == 0)
 		return OPT_SEC;
+	if (strcmp(token, "force_pam") == 0)
+		return OPT_FORCE_PAM;
 	if (strcmp(token, "ip") == 0 ||
 		strcmp(token, "addr") == 0)
 		return OPT_IP;
@@ -891,12 +899,19 @@ 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;
 
+#ifdef HAVE_KRB5PAM
+		case OPT_FORCE_PAM:
+			parsed_info->force_pam = 1;
+			goto nocopy;
+#endif /* HAVE_KRB5PAM */
+			
 		case OPT_IP:
 			if (!value || !*value) {
 				fprintf(stderr,
@@ -1809,6 +1824,119 @@ 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 (fgets(reply[i].resp, MOUNT_PASSWD_SIZE + 1, stdin) == NULL)
+				goto fail;
+
+			reply[i].resp[MOUNT_PASSWD_SIZE] = '\0';
+			null_terminate_endl(reply[i].resp);
+
+			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++) {
+		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,
@@ -1891,7 +2019,31 @@ assemble_mountinfo(struct parsed_mount_info *parsed_info,
 		parsed_info->got_user = 1;
 	}
 
-	if (!parsed_info->got_password) {
+#ifdef HAVE_KRB5PAM
+	if (parsed_info->is_krb5 && parsed_info->force_pam) {
+		/*
+		 * 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.
+			 */
+			rc = 0;
+		}
+
+		parsed_info->got_password = 1;
+	}
+#endif /* HAVE_KRB5PAM */
+
+	/* If sec=krb5, then password is collected by other means */
+	if (!parsed_info->is_krb5 && !parsed_info->got_password) {
 		char tmp_pass[MOUNT_PASSWD_SIZE + 1];
 		char *prompt = NULL;
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH][SMB3] mount.cifs integration with PAM
  2020-09-24 10:39                     ` Shyam Prasad N
@ 2020-11-09 23:42                       ` Pavel Shilovsky
  2020-11-10 13:20                         ` Shyam Prasad N
  0 siblings, 1 reply; 17+ messages in thread
From: Pavel Shilovsky @ 2020-11-09 23:42 UTC (permalink / raw)
  To: Shyam Prasad N
  Cc: Aurélien Aptel, CIFS, samba-technical, Steve French, sribhat.msa

Merged into next. Please let me know if something needs to be fixed. Thanks!
--
Best regards,
Pavel Shilovsky

чт, 24 сент. 2020 г. в 03:39, Shyam Prasad N <nspmangalore@gmail.com>:
>
> Hi Aurélien,
>
> I've implemented most of your review comments. Also fixed the issue.
>
> On Wed, Sep 23, 2020 at 7:26 PM Aurélien Aptel <aaptel@suse.com> wrote:
> >
> > Shyam Prasad N <nspmangalore@gmail.com> writes:
> > > Also, I'll test this out with DFS once I figure out how to set it up. :)
> > > Re-attaching the patch with some minor changes with just the
> > > "force_pam" mount option.
> >
> > You will need 2 Windows VM. DFS is basically symlinks across
> > servers. The DFS root VM will host the links (standalone namespace) and
> > you have to make them point to shares on the 2nd VM. You don't need to
> > setup replication to test.
> >
> > When you mount the root in cifs.ko and access a link, the server will
> > reply that the file is remote. cifs.ko then does an FSCTL on the link to
> > resolve the target it points to and then connects to the target and
> > mounts it under the link seemlessly.
> >
> >
> > Regarding the patch:
> >
> > * need to update the man page with option and explanation
> >
> > I have some comments with the style, I know it's annoying.. but it
> > would be best to keep the same across the code.
> >
> > * use the existing indent style (tabs) and avoid adding trailing whitespaces.
> > * no () for return statements
> > * no casting for memory allocation
> > * if (X) free(X)  => free(X)
> >
> > Below some comments about pam_auth_krb5_conv():
> >
> > > @@ -1809,6 +1824,119 @@ 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 (fgets(reply[i].resp, MOUNT_PASSWD_SIZE + 1, stdin) == NULL)
> >
> > Do we need to remove the trailing \n from the buffer?
> >
> > > +                goto fail;
> > > +
> > > +            reply[i].resp[MOUNT_PASSWD_SIZE] = '\0';
> > > +
> > > +                     reply[i].resp_retcode = PAM_SUCCESS;
> > > +                     break;
> > > +             case PAM_ERROR_MSG:
> >
> > Shouldn't this PAM_ERROR_MSG case goto fail?
> >
> > > +             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(NULL) is a no-op, remove the checks.
> >
> > > +     }
> > > +     free(reply);
> > > +     return (PAM_CONV_ERR);
> > > +}
> >
> > I gave this a try with a properly configured system joined to AD from
> > local root account:
> >
> > aaptel$ ./configure
> > ...
> > checking krb5.h usability... yes
> > checking krb5.h presence... yes
> > checking for krb5.h... yes
> > checking krb5/krb5.h usability... yes
> > checking krb5/krb5.h presence... yes
> > checking for krb5/krb5.h... yes
> > checking for keyvalue in krb5_keyblock... no
> > ...
> > checking keyutils.h usability... yes
> > checking keyutils.h presence... yes
> > checking for keyutils.h... yes
> > checking for krb5_init_context in -lkrb5... yes
> > ...
> > checking for WBCLIENT... yes
> > checking for wbcSidsToUnixIds in -lwbclient... yes
> > ...
> > checking for keyutils.h... (cached) yes
> > checking security/pam_appl.h usability... yes
> > checking security/pam_appl.h presence... yes
> > checking for security/pam_appl.h... yes
> > checking for pam_start in -lpam... yes
> > checking for security/pam_appl.h... (cached) yes
> > checking for krb5_auth_con_getsendsubkey... yes
> > checking for krb5_principal_get_realm... no
> > checking for krb5_free_unparsed_name... yes
> > checking for krb5_auth_con_setaddrs... yes
> > checking for krb5_auth_con_set_req_cksumtype... yes
> > ...
> > aaptel$ make
> > ....(ok)
> >
> > Without force_pam:
> >
> > root# ./mount.cifs -v //adnuc.nuc.test/data /x -o sec=krb5,username=administrator,domain=NUC
> > mount.cifs kernel mount options: ip=192.168.2.111,unc=\\adnuc.nuc.test\data,sec=krb5,user=administrator,domain=NUC
> > mount error(2): No such file or directory
> > Refer to the mount.cifs(8) manual page (e.g. man mount.cifs) and kernel log messages (dmesg)
> >
> > With force_pam:
> >
> > root# ./mount.cifs -v //adnuc.nuc.test/data /x -o sec=krb5,username=administrator,domain=NUC,force_pam
> > Authenticating as user: administrator
> > Error in authenticating user with PAM: Authentication failure
> > Attempt to authenticate user with PAM unsuccessful. Still, proceeding with mount.
> >
> > => no further message but mount failed and no msg in dmesg, it didn't
> >    reach the mount() call
> >
> > Not sure what is going on. Does the domain need to be passed to PAM?
> >
> > Cheers,
> > --
> > Aurélien Aptel / SUSE Labs Samba Team
> > GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
> > SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
> > GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)
>
>
>
> --
> -Shyam

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH][SMB3] mount.cifs integration with PAM
  2020-11-09 23:42                       ` Pavel Shilovsky
@ 2020-11-10 13:20                         ` Shyam Prasad N
  2020-11-10 19:22                           ` Pavel Shilovsky
  0 siblings, 1 reply; 17+ messages in thread
From: Shyam Prasad N @ 2020-11-10 13:20 UTC (permalink / raw)
  To: Pavel Shilovsky
  Cc: Aurélien Aptel, CIFS, samba-technical, Steve French, sribhat.msa

Hi Pavel,

There is more that needs to be done on this item. Otherwise, this will
depend on user behaviour to cleanup unused krb5 tickets.
The pending items on this is to propagate this mount option to cifs.ko
and write an umount.cifs utility to read that mount option to do PAM
logoff.
So please rollback this patch for now.

Regards,
Shyam

On Tue, Nov 10, 2020 at 5:12 AM Pavel Shilovsky <piastryyy@gmail.com> wrote:
>
> Merged into next. Please let me know if something needs to be fixed. Thanks!
> --
> Best regards,
> Pavel Shilovsky
>
> чт, 24 сент. 2020 г. в 03:39, Shyam Prasad N <nspmangalore@gmail.com>:
> >
> > Hi Aurélien,
> >
> > I've implemented most of your review comments. Also fixed the issue.
> >
> > On Wed, Sep 23, 2020 at 7:26 PM Aurélien Aptel <aaptel@suse.com> wrote:
> > >
> > > Shyam Prasad N <nspmangalore@gmail.com> writes:
> > > > Also, I'll test this out with DFS once I figure out how to set it up. :)
> > > > Re-attaching the patch with some minor changes with just the
> > > > "force_pam" mount option.
> > >
> > > You will need 2 Windows VM. DFS is basically symlinks across
> > > servers. The DFS root VM will host the links (standalone namespace) and
> > > you have to make them point to shares on the 2nd VM. You don't need to
> > > setup replication to test.
> > >
> > > When you mount the root in cifs.ko and access a link, the server will
> > > reply that the file is remote. cifs.ko then does an FSCTL on the link to
> > > resolve the target it points to and then connects to the target and
> > > mounts it under the link seemlessly.
> > >
> > >
> > > Regarding the patch:
> > >
> > > * need to update the man page with option and explanation
> > >
> > > I have some comments with the style, I know it's annoying.. but it
> > > would be best to keep the same across the code.
> > >
> > > * use the existing indent style (tabs) and avoid adding trailing whitespaces.
> > > * no () for return statements
> > > * no casting for memory allocation
> > > * if (X) free(X)  => free(X)
> > >
> > > Below some comments about pam_auth_krb5_conv():
> > >
> > > > @@ -1809,6 +1824,119 @@ 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 (fgets(reply[i].resp, MOUNT_PASSWD_SIZE + 1, stdin) == NULL)
> > >
> > > Do we need to remove the trailing \n from the buffer?
> > >
> > > > +                goto fail;
> > > > +
> > > > +            reply[i].resp[MOUNT_PASSWD_SIZE] = '\0';
> > > > +
> > > > +                     reply[i].resp_retcode = PAM_SUCCESS;
> > > > +                     break;
> > > > +             case PAM_ERROR_MSG:
> > >
> > > Shouldn't this PAM_ERROR_MSG case goto fail?
> > >
> > > > +             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(NULL) is a no-op, remove the checks.
> > >
> > > > +     }
> > > > +     free(reply);
> > > > +     return (PAM_CONV_ERR);
> > > > +}
> > >
> > > I gave this a try with a properly configured system joined to AD from
> > > local root account:
> > >
> > > aaptel$ ./configure
> > > ...
> > > checking krb5.h usability... yes
> > > checking krb5.h presence... yes
> > > checking for krb5.h... yes
> > > checking krb5/krb5.h usability... yes
> > > checking krb5/krb5.h presence... yes
> > > checking for krb5/krb5.h... yes
> > > checking for keyvalue in krb5_keyblock... no
> > > ...
> > > checking keyutils.h usability... yes
> > > checking keyutils.h presence... yes
> > > checking for keyutils.h... yes
> > > checking for krb5_init_context in -lkrb5... yes
> > > ...
> > > checking for WBCLIENT... yes
> > > checking for wbcSidsToUnixIds in -lwbclient... yes
> > > ...
> > > checking for keyutils.h... (cached) yes
> > > checking security/pam_appl.h usability... yes
> > > checking security/pam_appl.h presence... yes
> > > checking for security/pam_appl.h... yes
> > > checking for pam_start in -lpam... yes
> > > checking for security/pam_appl.h... (cached) yes
> > > checking for krb5_auth_con_getsendsubkey... yes
> > > checking for krb5_principal_get_realm... no
> > > checking for krb5_free_unparsed_name... yes
> > > checking for krb5_auth_con_setaddrs... yes
> > > checking for krb5_auth_con_set_req_cksumtype... yes
> > > ...
> > > aaptel$ make
> > > ....(ok)
> > >
> > > Without force_pam:
> > >
> > > root# ./mount.cifs -v //adnuc.nuc.test/data /x -o sec=krb5,username=administrator,domain=NUC
> > > mount.cifs kernel mount options: ip=192.168.2.111,unc=\\adnuc.nuc.test\data,sec=krb5,user=administrator,domain=NUC
> > > mount error(2): No such file or directory
> > > Refer to the mount.cifs(8) manual page (e.g. man mount.cifs) and kernel log messages (dmesg)
> > >
> > > With force_pam:
> > >
> > > root# ./mount.cifs -v //adnuc.nuc.test/data /x -o sec=krb5,username=administrator,domain=NUC,force_pam
> > > Authenticating as user: administrator
> > > Error in authenticating user with PAM: Authentication failure
> > > Attempt to authenticate user with PAM unsuccessful. Still, proceeding with mount.
> > >
> > > => no further message but mount failed and no msg in dmesg, it didn't
> > >    reach the mount() call
> > >
> > > Not sure what is going on. Does the domain need to be passed to PAM?
> > >
> > > Cheers,
> > > --
> > > Aurélien Aptel / SUSE Labs Samba Team
> > > GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
> > > SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
> > > GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)
> >
> >
> >
> > --
> > -Shyam



-- 
-Shyam

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH][SMB3] mount.cifs integration with PAM
  2020-11-10 13:20                         ` Shyam Prasad N
@ 2020-11-10 19:22                           ` Pavel Shilovsky
  2020-11-27 10:43                             ` Shyam Prasad N
  0 siblings, 1 reply; 17+ messages in thread
From: Pavel Shilovsky @ 2020-11-10 19:22 UTC (permalink / raw)
  To: Shyam Prasad N
  Cc: Aurélien Aptel, CIFS, samba-technical, Steve French, sribhat.msa

Sure. Removed the patch and updated the next branch.
--
Best regards,
Pavel Shilovsky

вт, 10 нояб. 2020 г. в 05:20, Shyam Prasad N <nspmangalore@gmail.com>:
>
> Hi Pavel,
>
> There is more that needs to be done on this item. Otherwise, this will
> depend on user behaviour to cleanup unused krb5 tickets.
> The pending items on this is to propagate this mount option to cifs.ko
> and write an umount.cifs utility to read that mount option to do PAM
> logoff.
> So please rollback this patch for now.
>
> Regards,
> Shyam
>
> On Tue, Nov 10, 2020 at 5:12 AM Pavel Shilovsky <piastryyy@gmail.com> wrote:
> >
> > Merged into next. Please let me know if something needs to be fixed. Thanks!
> > --
> > Best regards,
> > Pavel Shilovsky
> >
> > чт, 24 сент. 2020 г. в 03:39, Shyam Prasad N <nspmangalore@gmail.com>:
> > >
> > > Hi Aurélien,
> > >
> > > I've implemented most of your review comments. Also fixed the issue.
> > >
> > > On Wed, Sep 23, 2020 at 7:26 PM Aurélien Aptel <aaptel@suse.com> wrote:
> > > >
> > > > Shyam Prasad N <nspmangalore@gmail.com> writes:
> > > > > Also, I'll test this out with DFS once I figure out how to set it up. :)
> > > > > Re-attaching the patch with some minor changes with just the
> > > > > "force_pam" mount option.
> > > >
> > > > You will need 2 Windows VM. DFS is basically symlinks across
> > > > servers. The DFS root VM will host the links (standalone namespace) and
> > > > you have to make them point to shares on the 2nd VM. You don't need to
> > > > setup replication to test.
> > > >
> > > > When you mount the root in cifs.ko and access a link, the server will
> > > > reply that the file is remote. cifs.ko then does an FSCTL on the link to
> > > > resolve the target it points to and then connects to the target and
> > > > mounts it under the link seemlessly.
> > > >
> > > >
> > > > Regarding the patch:
> > > >
> > > > * need to update the man page with option and explanation
> > > >
> > > > I have some comments with the style, I know it's annoying.. but it
> > > > would be best to keep the same across the code.
> > > >
> > > > * use the existing indent style (tabs) and avoid adding trailing whitespaces.
> > > > * no () for return statements
> > > > * no casting for memory allocation
> > > > * if (X) free(X)  => free(X)
> > > >
> > > > Below some comments about pam_auth_krb5_conv():
> > > >
> > > > > @@ -1809,6 +1824,119 @@ 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 (fgets(reply[i].resp, MOUNT_PASSWD_SIZE + 1, stdin) == NULL)
> > > >
> > > > Do we need to remove the trailing \n from the buffer?
> > > >
> > > > > +                goto fail;
> > > > > +
> > > > > +            reply[i].resp[MOUNT_PASSWD_SIZE] = '\0';
> > > > > +
> > > > > +                     reply[i].resp_retcode = PAM_SUCCESS;
> > > > > +                     break;
> > > > > +             case PAM_ERROR_MSG:
> > > >
> > > > Shouldn't this PAM_ERROR_MSG case goto fail?
> > > >
> > > > > +             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(NULL) is a no-op, remove the checks.
> > > >
> > > > > +     }
> > > > > +     free(reply);
> > > > > +     return (PAM_CONV_ERR);
> > > > > +}
> > > >
> > > > I gave this a try with a properly configured system joined to AD from
> > > > local root account:
> > > >
> > > > aaptel$ ./configure
> > > > ...
> > > > checking krb5.h usability... yes
> > > > checking krb5.h presence... yes
> > > > checking for krb5.h... yes
> > > > checking krb5/krb5.h usability... yes
> > > > checking krb5/krb5.h presence... yes
> > > > checking for krb5/krb5.h... yes
> > > > checking for keyvalue in krb5_keyblock... no
> > > > ...
> > > > checking keyutils.h usability... yes
> > > > checking keyutils.h presence... yes
> > > > checking for keyutils.h... yes
> > > > checking for krb5_init_context in -lkrb5... yes
> > > > ...
> > > > checking for WBCLIENT... yes
> > > > checking for wbcSidsToUnixIds in -lwbclient... yes
> > > > ...
> > > > checking for keyutils.h... (cached) yes
> > > > checking security/pam_appl.h usability... yes
> > > > checking security/pam_appl.h presence... yes
> > > > checking for security/pam_appl.h... yes
> > > > checking for pam_start in -lpam... yes
> > > > checking for security/pam_appl.h... (cached) yes
> > > > checking for krb5_auth_con_getsendsubkey... yes
> > > > checking for krb5_principal_get_realm... no
> > > > checking for krb5_free_unparsed_name... yes
> > > > checking for krb5_auth_con_setaddrs... yes
> > > > checking for krb5_auth_con_set_req_cksumtype... yes
> > > > ...
> > > > aaptel$ make
> > > > ....(ok)
> > > >
> > > > Without force_pam:
> > > >
> > > > root# ./mount.cifs -v //adnuc.nuc.test/data /x -o sec=krb5,username=administrator,domain=NUC
> > > > mount.cifs kernel mount options: ip=192.168.2.111,unc=\\adnuc.nuc.test\data,sec=krb5,user=administrator,domain=NUC
> > > > mount error(2): No such file or directory
> > > > Refer to the mount.cifs(8) manual page (e.g. man mount.cifs) and kernel log messages (dmesg)
> > > >
> > > > With force_pam:
> > > >
> > > > root# ./mount.cifs -v //adnuc.nuc.test/data /x -o sec=krb5,username=administrator,domain=NUC,force_pam
> > > > Authenticating as user: administrator
> > > > Error in authenticating user with PAM: Authentication failure
> > > > Attempt to authenticate user with PAM unsuccessful. Still, proceeding with mount.
> > > >
> > > > => no further message but mount failed and no msg in dmesg, it didn't
> > > >    reach the mount() call
> > > >
> > > > Not sure what is going on. Does the domain need to be passed to PAM?
> > > >
> > > > Cheers,
> > > > --
> > > > Aurélien Aptel / SUSE Labs Samba Team
> > > > GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
> > > > SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
> > > > GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)
> > >
> > >
> > >
> > > --
> > > -Shyam
>
>
>
> --
> -Shyam

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH][SMB3] mount.cifs integration with PAM
  2020-11-10 19:22                           ` Pavel Shilovsky
@ 2020-11-27 10:43                             ` Shyam Prasad N
  2020-12-14 18:03                               ` Stefan Metzmacher
  0 siblings, 1 reply; 17+ messages in thread
From: Shyam Prasad N @ 2020-11-27 10:43 UTC (permalink / raw)
  To: Pavel Shilovsky
  Cc: Aurélien Aptel, CIFS, samba-technical, Steve French, sribhat.msa

Discussed this with Aurelien today.

With the patch last sent, users are authenticated using the PAM stack.
However, there's no call to cleanup the PAM credentials. Which could
leave the kerberos tickets around, even after the umount.

To complete this fix, we need a mechanism to tell the umount helper
umount.cifs (a new executable to be created) that PAM authentication
was used for the mount.

There are two possible approaches which I could think of:
1. Add a new mount option in cifs.ko. Inside cifs.ko, this option
would be non-functional. But will be used by umount.cifs to call PAM
for cleanup.
2. In mount.cifs, keep this info in a temporary file (maybe
/var/run/cifs/). umount.cifs will read this and call PAM for cleanup.

I feel approach 1 is a cleaner approach to take. Aurelien seems to
favour option 1 as well.
Please feel free to comment if you feel otherwise.
Once we agree on the right approach, I'll send a patch for the changes.

Regards,
Shyam

On Wed, Nov 11, 2020 at 12:53 AM Pavel Shilovsky <piastryyy@gmail.com> wrote:
>
> Sure. Removed the patch and updated the next branch.
> --
> Best regards,
> Pavel Shilovsky
>
> вт, 10 нояб. 2020 г. в 05:20, Shyam Prasad N <nspmangalore@gmail.com>:
> >
> > Hi Pavel,
> >
> > There is more that needs to be done on this item. Otherwise, this will
> > depend on user behaviour to cleanup unused krb5 tickets.
> > The pending items on this is to propagate this mount option to cifs.ko
> > and write an umount.cifs utility to read that mount option to do PAM
> > logoff.
> > So please rollback this patch for now.
> >
> > Regards,
> > Shyam
> >
> > On Tue, Nov 10, 2020 at 5:12 AM Pavel Shilovsky <piastryyy@gmail.com> wrote:
> > >
> > > Merged into next. Please let me know if something needs to be fixed. Thanks!
> > > --
> > > Best regards,
> > > Pavel Shilovsky
> > >
> > > чт, 24 сент. 2020 г. в 03:39, Shyam Prasad N <nspmangalore@gmail.com>:
> > > >
> > > > Hi Aurélien,
> > > >
> > > > I've implemented most of your review comments. Also fixed the issue.
> > > >
> > > > On Wed, Sep 23, 2020 at 7:26 PM Aurélien Aptel <aaptel@suse.com> wrote:
> > > > >
> > > > > Shyam Prasad N <nspmangalore@gmail.com> writes:
> > > > > > Also, I'll test this out with DFS once I figure out how to set it up. :)
> > > > > > Re-attaching the patch with some minor changes with just the
> > > > > > "force_pam" mount option.
> > > > >
> > > > > You will need 2 Windows VM. DFS is basically symlinks across
> > > > > servers. The DFS root VM will host the links (standalone namespace) and
> > > > > you have to make them point to shares on the 2nd VM. You don't need to
> > > > > setup replication to test.
> > > > >
> > > > > When you mount the root in cifs.ko and access a link, the server will
> > > > > reply that the file is remote. cifs.ko then does an FSCTL on the link to
> > > > > resolve the target it points to and then connects to the target and
> > > > > mounts it under the link seemlessly.
> > > > >
> > > > >
> > > > > Regarding the patch:
> > > > >
> > > > > * need to update the man page with option and explanation
> > > > >
> > > > > I have some comments with the style, I know it's annoying.. but it
> > > > > would be best to keep the same across the code.
> > > > >
> > > > > * use the existing indent style (tabs) and avoid adding trailing whitespaces.
> > > > > * no () for return statements
> > > > > * no casting for memory allocation
> > > > > * if (X) free(X)  => free(X)
> > > > >
> > > > > Below some comments about pam_auth_krb5_conv():
> > > > >
> > > > > > @@ -1809,6 +1824,119 @@ 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 (fgets(reply[i].resp, MOUNT_PASSWD_SIZE + 1, stdin) == NULL)
> > > > >
> > > > > Do we need to remove the trailing \n from the buffer?
> > > > >
> > > > > > +                goto fail;
> > > > > > +
> > > > > > +            reply[i].resp[MOUNT_PASSWD_SIZE] = '\0';
> > > > > > +
> > > > > > +                     reply[i].resp_retcode = PAM_SUCCESS;
> > > > > > +                     break;
> > > > > > +             case PAM_ERROR_MSG:
> > > > >
> > > > > Shouldn't this PAM_ERROR_MSG case goto fail?
> > > > >
> > > > > > +             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(NULL) is a no-op, remove the checks.
> > > > >
> > > > > > +     }
> > > > > > +     free(reply);
> > > > > > +     return (PAM_CONV_ERR);
> > > > > > +}
> > > > >
> > > > > I gave this a try with a properly configured system joined to AD from
> > > > > local root account:
> > > > >
> > > > > aaptel$ ./configure
> > > > > ...
> > > > > checking krb5.h usability... yes
> > > > > checking krb5.h presence... yes
> > > > > checking for krb5.h... yes
> > > > > checking krb5/krb5.h usability... yes
> > > > > checking krb5/krb5.h presence... yes
> > > > > checking for krb5/krb5.h... yes
> > > > > checking for keyvalue in krb5_keyblock... no
> > > > > ...
> > > > > checking keyutils.h usability... yes
> > > > > checking keyutils.h presence... yes
> > > > > checking for keyutils.h... yes
> > > > > checking for krb5_init_context in -lkrb5... yes
> > > > > ...
> > > > > checking for WBCLIENT... yes
> > > > > checking for wbcSidsToUnixIds in -lwbclient... yes
> > > > > ...
> > > > > checking for keyutils.h... (cached) yes
> > > > > checking security/pam_appl.h usability... yes
> > > > > checking security/pam_appl.h presence... yes
> > > > > checking for security/pam_appl.h... yes
> > > > > checking for pam_start in -lpam... yes
> > > > > checking for security/pam_appl.h... (cached) yes
> > > > > checking for krb5_auth_con_getsendsubkey... yes
> > > > > checking for krb5_principal_get_realm... no
> > > > > checking for krb5_free_unparsed_name... yes
> > > > > checking for krb5_auth_con_setaddrs... yes
> > > > > checking for krb5_auth_con_set_req_cksumtype... yes
> > > > > ...
> > > > > aaptel$ make
> > > > > ....(ok)
> > > > >
> > > > > Without force_pam:
> > > > >
> > > > > root# ./mount.cifs -v //adnuc.nuc.test/data /x -o sec=krb5,username=administrator,domain=NUC
> > > > > mount.cifs kernel mount options: ip=192.168.2.111,unc=\\adnuc.nuc.test\data,sec=krb5,user=administrator,domain=NUC
> > > > > mount error(2): No such file or directory
> > > > > Refer to the mount.cifs(8) manual page (e.g. man mount.cifs) and kernel log messages (dmesg)
> > > > >
> > > > > With force_pam:
> > > > >
> > > > > root# ./mount.cifs -v //adnuc.nuc.test/data /x -o sec=krb5,username=administrator,domain=NUC,force_pam
> > > > > Authenticating as user: administrator
> > > > > Error in authenticating user with PAM: Authentication failure
> > > > > Attempt to authenticate user with PAM unsuccessful. Still, proceeding with mount.
> > > > >
> > > > > => no further message but mount failed and no msg in dmesg, it didn't
> > > > >    reach the mount() call
> > > > >
> > > > > Not sure what is going on. Does the domain need to be passed to PAM?
> > > > >
> > > > > Cheers,
> > > > > --
> > > > > Aurélien Aptel / SUSE Labs Samba Team
> > > > > GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
> > > > > SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
> > > > > GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)
> > > >
> > > >
> > > >
> > > > --
> > > > -Shyam
> >
> >
> >
> > --
> > -Shyam



-- 
-Shyam

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH][SMB3] mount.cifs integration with PAM
  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>
  0 siblings, 1 reply; 17+ messages in thread
From: Stefan Metzmacher @ 2020-12-14 18:03 UTC (permalink / raw)
  To: Shyam Prasad N, Pavel Shilovsky
  Cc: CIFS, sribhat.msa, samba-technical, Aurélien Aptel, Steve French


[-- Attachment #1.1: Type: text/plain, Size: 10297 bytes --]

Hi Shyam,

in what way is this specific to kerberos?

Would it make sense to call the configure option just --enable-pam?
And also remove the 'krb5' from other variables?

PAM_CIFS_SERVICE "cifs" seems to be unspecific,
"mount.cifs" or "cifs.mount". Maybe even "mount.smb3"?

Can you fix the indentation of the patch? There seems to be
a strange mix of leading tabs and whitespaces, which make it hard to
read the patch.

With force_pam I would not expect a failure to be ignored.

Why can this only be used with sec=krb5? Wouldn't it be possible
to fetch the password from the pam stack in order to pass it to
the kernel?

metze

Am 27.11.20 um 11:43 schrieb Shyam Prasad N via samba-technical:
> Discussed this with Aurelien today.
> 
> With the patch last sent, users are authenticated using the PAM stack.
> However, there's no call to cleanup the PAM credentials. Which could
> leave the kerberos tickets around, even after the umount.
> 
> To complete this fix, we need a mechanism to tell the umount helper
> umount.cifs (a new executable to be created) that PAM authentication
> was used for the mount.
> 
> There are two possible approaches which I could think of:
> 1. Add a new mount option in cifs.ko. Inside cifs.ko, this option
> would be non-functional. But will be used by umount.cifs to call PAM
> for cleanup.
> 2. In mount.cifs, keep this info in a temporary file (maybe
> /var/run/cifs/). umount.cifs will read this and call PAM for cleanup.
> 
> I feel approach 1 is a cleaner approach to take. Aurelien seems to
> favour option 1 as well.
> Please feel free to comment if you feel otherwise.
> Once we agree on the right approach, I'll send a patch for the changes.
> 
> Regards,
> Shyam
> 
> On Wed, Nov 11, 2020 at 12:53 AM Pavel Shilovsky <piastryyy@gmail.com> wrote:
>>
>> Sure. Removed the patch and updated the next branch.
>> --
>> Best regards,
>> Pavel Shilovsky
>>
>> вт, 10 нояб. 2020 г. в 05:20, Shyam Prasad N <nspmangalore@gmail.com>:
>>>
>>> Hi Pavel,
>>>
>>> There is more that needs to be done on this item. Otherwise, this will
>>> depend on user behaviour to cleanup unused krb5 tickets.
>>> The pending items on this is to propagate this mount option to cifs.ko
>>> and write an umount.cifs utility to read that mount option to do PAM
>>> logoff.
>>> So please rollback this patch for now.
>>>
>>> Regards,
>>> Shyam
>>>
>>> On Tue, Nov 10, 2020 at 5:12 AM Pavel Shilovsky <piastryyy@gmail.com> wrote:
>>>>
>>>> Merged into next. Please let me know if something needs to be fixed. Thanks!
>>>> --
>>>> Best regards,
>>>> Pavel Shilovsky
>>>>
>>>> чт, 24 сент. 2020 г. в 03:39, Shyam Prasad N <nspmangalore@gmail.com>:
>>>>>
>>>>> Hi Aurélien,
>>>>>
>>>>> I've implemented most of your review comments. Also fixed the issue.
>>>>>
>>>>> On Wed, Sep 23, 2020 at 7:26 PM Aurélien Aptel <aaptel@suse.com> wrote:
>>>>>>
>>>>>> Shyam Prasad N <nspmangalore@gmail.com> writes:
>>>>>>> Also, I'll test this out with DFS once I figure out how to set it up. :)
>>>>>>> Re-attaching the patch with some minor changes with just the
>>>>>>> "force_pam" mount option.
>>>>>>
>>>>>> You will need 2 Windows VM. DFS is basically symlinks across
>>>>>> servers. The DFS root VM will host the links (standalone namespace) and
>>>>>> you have to make them point to shares on the 2nd VM. You don't need to
>>>>>> setup replication to test.
>>>>>>
>>>>>> When you mount the root in cifs.ko and access a link, the server will
>>>>>> reply that the file is remote. cifs.ko then does an FSCTL on the link to
>>>>>> resolve the target it points to and then connects to the target and
>>>>>> mounts it under the link seemlessly.
>>>>>>
>>>>>>
>>>>>> Regarding the patch:
>>>>>>
>>>>>> * need to update the man page with option and explanation
>>>>>>
>>>>>> I have some comments with the style, I know it's annoying.. but it
>>>>>> would be best to keep the same across the code.
>>>>>>
>>>>>> * use the existing indent style (tabs) and avoid adding trailing whitespaces.
>>>>>> * no () for return statements
>>>>>> * no casting for memory allocation
>>>>>> * if (X) free(X)  => free(X)
>>>>>>
>>>>>> Below some comments about pam_auth_krb5_conv():
>>>>>>
>>>>>>> @@ -1809,6 +1824,119 @@ 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 (fgets(reply[i].resp, MOUNT_PASSWD_SIZE + 1, stdin) == NULL)
>>>>>>
>>>>>> Do we need to remove the trailing \n from the buffer?
>>>>>>
>>>>>>> +                goto fail;
>>>>>>> +
>>>>>>> +            reply[i].resp[MOUNT_PASSWD_SIZE] = '\0';
>>>>>>> +
>>>>>>> +                     reply[i].resp_retcode = PAM_SUCCESS;
>>>>>>> +                     break;
>>>>>>> +             case PAM_ERROR_MSG:
>>>>>>
>>>>>> Shouldn't this PAM_ERROR_MSG case goto fail?
>>>>>>
>>>>>>> +             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(NULL) is a no-op, remove the checks.
>>>>>>
>>>>>>> +     }
>>>>>>> +     free(reply);
>>>>>>> +     return (PAM_CONV_ERR);
>>>>>>> +}
>>>>>>
>>>>>> I gave this a try with a properly configured system joined to AD from
>>>>>> local root account:
>>>>>>
>>>>>> aaptel$ ./configure
>>>>>> ...
>>>>>> checking krb5.h usability... yes
>>>>>> checking krb5.h presence... yes
>>>>>> checking for krb5.h... yes
>>>>>> checking krb5/krb5.h usability... yes
>>>>>> checking krb5/krb5.h presence... yes
>>>>>> checking for krb5/krb5.h... yes
>>>>>> checking for keyvalue in krb5_keyblock... no
>>>>>> ...
>>>>>> checking keyutils.h usability... yes
>>>>>> checking keyutils.h presence... yes
>>>>>> checking for keyutils.h... yes
>>>>>> checking for krb5_init_context in -lkrb5... yes
>>>>>> ...
>>>>>> checking for WBCLIENT... yes
>>>>>> checking for wbcSidsToUnixIds in -lwbclient... yes
>>>>>> ...
>>>>>> checking for keyutils.h... (cached) yes
>>>>>> checking security/pam_appl.h usability... yes
>>>>>> checking security/pam_appl.h presence... yes
>>>>>> checking for security/pam_appl.h... yes
>>>>>> checking for pam_start in -lpam... yes
>>>>>> checking for security/pam_appl.h... (cached) yes
>>>>>> checking for krb5_auth_con_getsendsubkey... yes
>>>>>> checking for krb5_principal_get_realm... no
>>>>>> checking for krb5_free_unparsed_name... yes
>>>>>> checking for krb5_auth_con_setaddrs... yes
>>>>>> checking for krb5_auth_con_set_req_cksumtype... yes
>>>>>> ...
>>>>>> aaptel$ make
>>>>>> ....(ok)
>>>>>>
>>>>>> Without force_pam:
>>>>>>
>>>>>> root# ./mount.cifs -v //adnuc.nuc.test/data /x -o sec=krb5,username=administrator,domain=NUC
>>>>>> mount.cifs kernel mount options: ip=192.168.2.111,unc=\\adnuc.nuc.test\data,sec=krb5,user=administrator,domain=NUC
>>>>>> mount error(2): No such file or directory
>>>>>> Refer to the mount.cifs(8) manual page (e.g. man mount.cifs) and kernel log messages (dmesg)
>>>>>>
>>>>>> With force_pam:
>>>>>>
>>>>>> root# ./mount.cifs -v //adnuc.nuc.test/data /x -o sec=krb5,username=administrator,domain=NUC,force_pam
>>>>>> Authenticating as user: administrator
>>>>>> Error in authenticating user with PAM: Authentication failure
>>>>>> Attempt to authenticate user with PAM unsuccessful. Still, proceeding with mount.
>>>>>>
>>>>>> => no further message but mount failed and no msg in dmesg, it didn't
>>>>>>    reach the mount() call
>>>>>>
>>>>>> Not sure what is going on. Does the domain need to be passed to PAM?
>>>>>>
>>>>>> Cheers,
>>>>>> --
>>>>>> Aurélien Aptel / SUSE Labs Samba Team
>>>>>> GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
>>>>>> SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
>>>>>> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)
>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> -Shyam
>>>
>>>
>>>
>>> --
>>> -Shyam
> 
> 
> 



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH][SMB3] mount.cifs integration with PAM
       [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
  0 siblings, 1 reply; 17+ messages in thread
From: Shyam Prasad N @ 2021-01-30 14:24 UTC (permalink / raw)
  To: Stefan Metzmacher
  Cc: Pavel Shilovsky, CIFS, sribhat.msa, samba-technical,
	Aurélien Aptel, Steve French

It just occurred to me that integrating with mount.cifs will not
suffice for a multiuser scenario.
It sounds like we need to modify cifscreds command to have a switch
for cifscreds command; if used in krb5 context, instead of dealing
with kernel keyring, we authenticate with PAM (for add) and call PAM
logoff (for clear).
If users are then missing krb5 tickets (logged in to ssh using private
keys), they can call cifscreds to get the tickets.

@Pavel Shilovsky @Aurélien Aptel Please let me know what you think
about this approach.
If you agree, I'll start working on the patch.

Regards,
Shyam

On Wed, Dec 16, 2020 at 5:05 PM Shyam Prasad N <nspmangalore@gmail.com> wrote:
>
> Hi Stefan,
>
> Thanks for the review. My replies inline...
>
> On Mon, 14 Dec, 2020, 23:33 Stefan Metzmacher, <metze@samba.org> wrote:
>>
>> Hi Shyam,
>>
>> in what way is this specific to kerberos?
>
> Cifs.ko expects Kerberos TGT to be present in cred cache before the user mounts the share. If winbind or sssd is configured, this would happen on most types of user login, by authentication against PAM, except for ssh private key auth (which is quite common). Hence trying to provide the user an option to authenticate during the mount.
>
> Do you see use cases outside of krb5, which can make use of this?
>
>> Would it make sense to call the configure option just --enable-pam?
>> And also remove the 'krb5' from other variables?
>
> There's already enable-pam which applies to ntlm multiuser scenario. Let me recheck if I can integrate with it somehow.
>>
>>
>> PAM_CIFS_SERVICE "cifs" seems to be unspecific,
>> "mount.cifs" or "cifs.mount". Maybe even "mount.smb3"?
>
> There could be cases where we need to do PAM auth even in cifs.upcall. For example, when another user tries to access a multiuser mount. I haven't coded it here though. So kept the name generic.
>>
>>
>> Can you fix the indentation of the patch? There seems to be
>> a strange mix of leading tabs and whitespaces, which make it hard to
>> read the patch.
>
> Will fix this in my follow up patch.
>>
>>
>> With force_pam I would not expect a failure to be ignored.
>
> Initially, I was thinking this could be a fallback option, in case mount fails with ENOKEY. But later changed it to a seperate option. Will fix this in my next patch.
>>
>>
>> Why can this only be used with sec=krb5? Wouldn't it be possible
>> to fetch the password from the pam stack in order to pass it to
>> the kernel?
>
> I don't know of any PAM API to do that. You could use a password without prompting the user, which I'm doing for cases where password has been passed as a mount option.
>>
>>
>> metze
>>
>> Am 27.11.20 um 11:43 schrieb Shyam Prasad N via samba-technical:
>> > Discussed this with Aurelien today.
>> >
>> > With the patch last sent, users are authenticated using the PAM stack.
>> > However, there's no call to cleanup the PAM credentials. Which could
>> > leave the kerberos tickets around, even after the umount.
>> >
>> > To complete this fix, we need a mechanism to tell the umount helper
>> > umount.cifs (a new executable to be created) that PAM authentication
>> > was used for the mount.
>> >
>> > There are two possible approaches which I could think of:
>> > 1. Add a new mount option in cifs.ko. Inside cifs.ko, this option
>> > would be non-functional. But will be used by umount.cifs to call PAM
>> > for cleanup.
>> > 2. In mount.cifs, keep this info in a temporary file (maybe
>> > /var/run/cifs/). umount.cifs will read this and call PAM for cleanup.
>> >
>> > I feel approach 1 is a cleaner approach to take. Aurelien seems to
>> > favour option 1 as well.
>> > Please feel free to comment if you feel otherwise.
>> > Once we agree on the right approach, I'll send a patch for the changes.
>> >
>> > Regards,
>> > Shyam
>> >
>> > On Wed, Nov 11, 2020 at 12:53 AM Pavel Shilovsky <piastryyy@gmail.com> wrote:
>> >>
>> >> Sure. Removed the patch and updated the next branch.
>> >> --
>> >> Best regards,
>> >> Pavel Shilovsky
>> >>
>> >> вт, 10 нояб. 2020 г. в 05:20, Shyam Prasad N <nspmangalore@gmail.com>:
>> >>>
>> >>> Hi Pavel,
>> >>>
>> >>> There is more that needs to be done on this item. Otherwise, this will
>> >>> depend on user behaviour to cleanup unused krb5 tickets.
>> >>> The pending items on this is to propagate this mount option to cifs.ko
>> >>> and write an umount.cifs utility to read that mount option to do PAM
>> >>> logoff.
>> >>> So please rollback this patch for now.
>> >>>
>> >>> Regards,
>> >>> Shyam
>> >>>
>> >>> On Tue, Nov 10, 2020 at 5:12 AM Pavel Shilovsky <piastryyy@gmail.com> wrote:
>> >>>>
>> >>>> Merged into next. Please let me know if something needs to be fixed. Thanks!
>> >>>> --
>> >>>> Best regards,
>> >>>> Pavel Shilovsky
>> >>>>
>> >>>> чт, 24 сент. 2020 г. в 03:39, Shyam Prasad N <nspmangalore@gmail.com>:
>> >>>>>
>> >>>>> Hi Aurélien,
>> >>>>>
>> >>>>> I've implemented most of your review comments. Also fixed the issue.
>> >>>>>
>> >>>>> On Wed, Sep 23, 2020 at 7:26 PM Aurélien Aptel <aaptel@suse.com> wrote:
>> >>>>>>
>> >>>>>> Shyam Prasad N <nspmangalore@gmail.com> writes:
>> >>>>>>> Also, I'll test this out with DFS once I figure out how to set it up. :)
>> >>>>>>> Re-attaching the patch with some minor changes with just the
>> >>>>>>> "force_pam" mount option.
>> >>>>>>
>> >>>>>> You will need 2 Windows VM. DFS is basically symlinks across
>> >>>>>> servers. The DFS root VM will host the links (standalone namespace) and
>> >>>>>> you have to make them point to shares on the 2nd VM. You don't need to
>> >>>>>> setup replication to test.
>> >>>>>>
>> >>>>>> When you mount the root in cifs.ko and access a link, the server will
>> >>>>>> reply that the file is remote. cifs.ko then does an FSCTL on the link to
>> >>>>>> resolve the target it points to and then connects to the target and
>> >>>>>> mounts it under the link seemlessly.
>> >>>>>>
>> >>>>>>
>> >>>>>> Regarding the patch:
>> >>>>>>
>> >>>>>> * need to update the man page with option and explanation
>> >>>>>>
>> >>>>>> I have some comments with the style, I know it's annoying.. but it
>> >>>>>> would be best to keep the same across the code.
>> >>>>>>
>> >>>>>> * use the existing indent style (tabs) and avoid adding trailing whitespaces.
>> >>>>>> * no () for return statements
>> >>>>>> * no casting for memory allocation
>> >>>>>> * if (X) free(X)  => free(X)
>> >>>>>>
>> >>>>>> Below some comments about pam_auth_krb5_conv():
>> >>>>>>
>> >>>>>>> @@ -1809,6 +1824,119 @@ 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 (fgets(reply[i].resp, MOUNT_PASSWD_SIZE + 1, stdin) == NULL)
>> >>>>>>
>> >>>>>> Do we need to remove the trailing \n from the buffer?
>> >>>>>>
>> >>>>>>> +                goto fail;
>> >>>>>>> +
>> >>>>>>> +            reply[i].resp[MOUNT_PASSWD_SIZE] = '\0';
>> >>>>>>> +
>> >>>>>>> +                     reply[i].resp_retcode = PAM_SUCCESS;
>> >>>>>>> +                     break;
>> >>>>>>> +             case PAM_ERROR_MSG:
>> >>>>>>
>> >>>>>> Shouldn't this PAM_ERROR_MSG case goto fail?
>> >>>>>>
>> >>>>>>> +             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(NULL) is a no-op, remove the checks.
>> >>>>>>
>> >>>>>>> +     }
>> >>>>>>> +     free(reply);
>> >>>>>>> +     return (PAM_CONV_ERR);
>> >>>>>>> +}
>> >>>>>>
>> >>>>>> I gave this a try with a properly configured system joined to AD from
>> >>>>>> local root account:
>> >>>>>>
>> >>>>>> aaptel$ ./configure
>> >>>>>> ...
>> >>>>>> checking krb5.h usability... yes
>> >>>>>> checking krb5.h presence... yes
>> >>>>>> checking for krb5.h... yes
>> >>>>>> checking krb5/krb5.h usability... yes
>> >>>>>> checking krb5/krb5.h presence... yes
>> >>>>>> checking for krb5/krb5.h... yes
>> >>>>>> checking for keyvalue in krb5_keyblock... no
>> >>>>>> ...
>> >>>>>> checking keyutils.h usability... yes
>> >>>>>> checking keyutils.h presence... yes
>> >>>>>> checking for keyutils.h... yes
>> >>>>>> checking for krb5_init_context in -lkrb5... yes
>> >>>>>> ...
>> >>>>>> checking for WBCLIENT... yes
>> >>>>>> checking for wbcSidsToUnixIds in -lwbclient... yes
>> >>>>>> ...
>> >>>>>> checking for keyutils.h... (cached) yes
>> >>>>>> checking security/pam_appl.h usability... yes
>> >>>>>> checking security/pam_appl.h presence... yes
>> >>>>>> checking for security/pam_appl.h... yes
>> >>>>>> checking for pam_start in -lpam... yes
>> >>>>>> checking for security/pam_appl.h... (cached) yes
>> >>>>>> checking for krb5_auth_con_getsendsubkey... yes
>> >>>>>> checking for krb5_principal_get_realm... no
>> >>>>>> checking for krb5_free_unparsed_name... yes
>> >>>>>> checking for krb5_auth_con_setaddrs... yes
>> >>>>>> checking for krb5_auth_con_set_req_cksumtype... yes
>> >>>>>> ...
>> >>>>>> aaptel$ make
>> >>>>>> ....(ok)
>> >>>>>>
>> >>>>>> Without force_pam:
>> >>>>>>
>> >>>>>> root# ./mount.cifs -v //adnuc.nuc.test/data /x -o sec=krb5,username=administrator,domain=NUC
>> >>>>>> mount.cifs kernel mount options: ip=192.168.2.111,unc=\\adnuc.nuc.test\data,sec=krb5,user=administrator,domain=NUC
>> >>>>>> mount error(2): No such file or directory
>> >>>>>> Refer to the mount.cifs(8) manual page (e.g. man mount.cifs) and kernel log messages (dmesg)
>> >>>>>>
>> >>>>>> With force_pam:
>> >>>>>>
>> >>>>>> root# ./mount.cifs -v //adnuc.nuc.test/data /x -o sec=krb5,username=administrator,domain=NUC,force_pam
>> >>>>>> Authenticating as user: administrator
>> >>>>>> Error in authenticating user with PAM: Authentication failure
>> >>>>>> Attempt to authenticate user with PAM unsuccessful. Still, proceeding with mount.
>> >>>>>>
>> >>>>>> => no further message but mount failed and no msg in dmesg, it didn't
>> >>>>>>    reach the mount() call
>> >>>>>>
>> >>>>>> Not sure what is going on. Does the domain need to be passed to PAM?
>> >>>>>>
>> >>>>>> Cheers,
>> >>>>>> --
>> >>>>>> Aurélien Aptel / SUSE Labs Samba Team
>> >>>>>> GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
>> >>>>>> SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
>> >>>>>> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)
>> >>>>>
>> >>>>>
>> >>>>>
>> >>>>> --
>> >>>>> -Shyam
>> >>>
>> >>>
>> >>>
>> >>> --
>> >>> -Shyam
>> >
>> >
>> >
>>
>>


-- 
Regards,
Shyam

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH][SMB3] mount.cifs integration with PAM
  2021-01-30 14:24                                   ` Shyam Prasad N
@ 2021-02-01 10:51                                     ` Aurélien Aptel
  0 siblings, 0 replies; 17+ messages in thread
From: Aurélien Aptel @ 2021-02-01 10:51 UTC (permalink / raw)
  To: Shyam Prasad N, Stefan Metzmacher
  Cc: Pavel Shilovsky, CIFS, sribhat.msa, samba-technical, Steve French

Shyam Prasad N <nspmangalore@gmail.com> writes:
> It just occurred to me that integrating with mount.cifs will not
> suffice for a multiuser scenario.
> It sounds like we need to modify cifscreds command to have a switch
> for cifscreds command; if used in krb5 context, instead of dealing
> with kernel keyring, we authenticate with PAM (for add) and call PAM
> logoff (for clear).
> If users are then missing krb5 tickets (logged in to ssh using private
> keys), they can call cifscreds to get the tickets.
>
> @Pavel Shilovsky @Aurélien Aptel Please let me know what you think
> about this approach.
> If you agree, I'll start working on the patch.

Hm what happens where there are multiple mounts with different auth type
on the same machine. e.g.

//host/share1 as userA in /mnt/1 via ntlmssp
//host/share2 as userA in /mnt/2 via krb

cifscreds should change both no?

Cheers,
-- 
Aurélien Aptel / SUSE Labs Samba Team
GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)


^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2021-02-01 10:53 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-14  5:45 [PATCH][SMB3] mount.cifs integration with PAM Shyam Prasad N
2020-08-14  9:52 ` 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

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.