[PATCH] Optionally allow pam_setcred to override gid

[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

 



I would like to allow pam_setcred/pam_sm_setcred to override the gid that is normally set for a user. Currently the openssh code calls do_pam_setcred then it sets the gid to the user's gid as listed in /etc/passwd, LDAP, or whatever regardless of what the pam module set it to. I would instead like a pam module to be able to set the gid with setgid() and not have it overwritten by openssh.

I wrote a patch that does just that by comparing getgid() before and after calling do_pam_setcred. If the gid changes it sets pw->gid to the new gid, which is used in later functions. I don't know if this is considered the proper way to achieve that behavior in a safe way but it seemed logical to me. The behavior is optional; PermitGidOverride=no is the default.

As for the reasoning, this is for a scheduled environment using Slurm. I am developing a pam module that "adopts" ssh processes into the appropriate batch job on the node. Users can launch jobs via Slurm that run with their gid as one of their supplementary groups. As part of the adoption of the ssh process, I would like to set the ssh process's gid equal to that of the job it is being adopted into.

Ryan
diff --git a/auth-pam.c b/auth-pam.c
index d789bad..24edac0 100644
--- a/auth-pam.c
+++ b/auth-pam.c
@@ -929,14 +929,17 @@ do_pam_set_tty(const char *tty)
 			fatal("PAM: failed to set PAM_TTY: %s",
 			    pam_strerror(sshpam_handle, sshpam_err));
 	}
 }
 
 void
-do_pam_setcred(int init)
+do_pam_setcred(int init, struct passwd *pw)
 {
+	gid_t gid_pre_setcred, gid_post_setcred;
+
+	gid_pre_setcred = getgid();
 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
 	    (const void *)&store_conv);
 	if (sshpam_err != PAM_SUCCESS)
 		fatal("PAM: failed to set PAM_CONV: %s",
 		    pam_strerror(sshpam_handle, sshpam_err));
 	if (init) {
@@ -945,12 +948,23 @@ do_pam_setcred(int init)
 	} else {
 		debug("PAM: reinitializing credentials");
 		sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED);
 	}
 	if (sshpam_err == PAM_SUCCESS) {
 		sshpam_cred_established = 1;
+		gid_post_setcred = getgid();
+
+		/* If PermitGidOverride=yes, persist the new gid if a PAM module
+		 * overrides it */
+		if (options.permit_gid_override &&
+				gid_pre_setcred != gid_post_setcred) {
+			verbose("Overriding gid to %u from %u",
+			(u_int)gid_post_setcred,
+			(u_int)gid_pre_setcred);
+			pw->pw_gid = gid_post_setcred;
+		}
 		return;
 	}
 	if (sshpam_authenticated)
 		fatal("PAM: pam_setcred(): %s",
 		    pam_strerror(sshpam_handle, sshpam_err));
 	else
diff --git a/auth-pam.h b/auth-pam.h
index a1a2b52..58ee0bb 100644
--- a/auth-pam.h
+++ b/auth-pam.h
@@ -33,13 +33,13 @@
 
 void start_pam(Authctxt *);
 void finish_pam(void);
 u_int do_pam_account(void);
 void do_pam_session(void);
 void do_pam_set_tty(const char *);
-void do_pam_setcred(int );
+void do_pam_setcred(int, struct passwd *);
 void do_pam_chauthtok(void);
 int do_pam_putenv(char *, char *);
 char ** fetch_pam_environment(void);
 char ** fetch_pam_child_environment(void);
 void free_pam_environment(char **);
 void sshpam_thread_cleanup(void);
diff --git a/platform.c b/platform.c
index ee313da..3fe688d 100644
--- a/platform.c
+++ b/platform.c
@@ -121,13 +121,13 @@ platform_setusercontext(struct passwd *pw)
 	/*
 	 * If we have both LOGIN_CAP and PAM, we want to establish creds
 	 * before calling setusercontext (in session.c:do_setusercontext).
 	 */
 	if (getuid() == 0 || geteuid() == 0) {
 		if (options.use_pam) {
-			do_pam_setcred(use_privsep);
+			do_pam_setcred(use_privsep, pw);
 		}
 	}
 # endif /* USE_PAM */
 
 #if !defined(HAVE_LOGIN_CAP) && defined(HAVE_GETLUID) && defined(HAVE_SETLUID)
 	if (getuid() == 0 || geteuid() == 0) {
@@ -149,13 +149,13 @@ platform_setusercontext_post_groups(struct passwd *pw)
 	/*
 	 * PAM credentials may take the form of supplementary groups.
 	 * These will have been wiped by the above initgroups() call.
 	 * Reestablish them here.
 	 */
 	if (options.use_pam) {
-		do_pam_setcred(use_privsep);
+		do_pam_setcred(use_privsep, pw);
 	}
 #endif /* USE_PAM */
 
 #if !defined(HAVE_LOGIN_CAP) && (defined(WITH_IRIX_PROJECT) || \
     defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY))
 	irix_setusercontext(pw);
diff --git a/servconf.c b/servconf.c
index 3185462..a6e6fe7 100644
--- a/servconf.c
+++ b/servconf.c
@@ -71,12 +71,13 @@ void
 initialize_server_options(ServerOptions *options)
 {
 	memset(options, 0, sizeof(*options));
 
 	/* Portable-specific options */
 	options->use_pam = -1;
+	options->permit_gid_override = -1;
 
 	/* Standard Options */
 	options->num_ports = 0;
 	options->ports_from_cmdline = 0;
 	options->listen_addrs = NULL;
 	options->address_family = -1;
@@ -177,12 +178,14 @@ fill_default_server_options(ServerOptions *options)
 {
 	int i;
 
 	/* Portable-specific options */
 	if (options->use_pam == -1)
 		options->use_pam = 0;
+	if (options->permit_gid_override == -1)
+		options->permit_gid_override = 0;
 
 	/* Standard Options */
 	if (options->protocol == SSH_PROTO_UNKNOWN)
 		options->protocol = SSH_PROTO_2;
 	if (options->num_host_key_files == 0) {
 		/* fill default hostkeys for protocols */
@@ -367,13 +370,13 @@ fill_default_server_options(ServerOptions *options)
 }
 
 /* Keyword tokens. */
 typedef enum {
 	sBadOption,		/* == unknown option */
 	/* Portable-specific options */
-	sUsePAM,
+	sUsePAM, sPermitGidOverride,
 	/* Standard Options */
 	sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime,
 	sKeyRegenerationTime, sPermitRootLogin, sLogFacility, sLogLevel,
 	sRhostsRSAAuthentication, sRSAAuthentication,
 	sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup,
 	sKerberosGetAFSToken,
@@ -414,12 +417,13 @@ static struct {
 	ServerOpCodes opcode;
 	u_int flags;
 } keywords[] = {
 	/* Portable-specific options */
 #ifdef USE_PAM
 	{ "usepam", sUsePAM, SSHCFG_GLOBAL },
+	{ "permitgidoverride", sPermitGidOverride, SSHCFG_GLOBAL },
 #else
 	{ "usepam", sUnsupported, SSHCFG_GLOBAL },
 #endif
 	{ "pamauthenticationviakbdint", sDeprecated, SSHCFG_GLOBAL },
 	/* Standard Options */
 	{ "port", sPort, SSHCFG_GLOBAL },
@@ -1227,12 +1231,16 @@ process_server_config_line(ServerOptions *options, char *line,
 		goto parse_flag;
 
 	case sPermitUserEnvironment:
 		intptr = &options->permit_user_env;
 		goto parse_flag;
 
+	case sPermitGidOverride:
+		intptr = &options->permit_gid_override;
+		goto parse_flag;
+
 	case sUseLogin:
 		intptr = &options->use_login;
 		goto parse_flag;
 
 	case sCompression:
 		intptr = &options->compression;
@@ -2092,12 +2100,13 @@ dump_config(ServerOptions *o)
 		}
 	}
 
 	/* integer arguments */
 #ifdef USE_PAM
 	dump_cfg_int(sUsePAM, o->use_pam);
+	dump_cfg_fmtint(sPermitGidOverride, o->permit_gid_override);
 #endif
 	dump_cfg_int(sServerKeyBits, o->server_key_bits);
 	dump_cfg_int(sLoginGraceTime, o->login_grace_time);
 	dump_cfg_int(sKeyRegenerationTime, o->key_regeneration_time);
 	dump_cfg_int(sX11DisplayOffset, o->x11_display_offset);
 	dump_cfg_int(sMaxAuthTries, o->max_authtries);
diff --git a/servconf.h b/servconf.h
index 9922f0c..630fb67 100644
--- a/servconf.h
+++ b/servconf.h
@@ -120,12 +120,13 @@ typedef struct {
 						 * authentication. */
 	int     kbd_interactive_authentication;	/* If true, permit */
 	int     challenge_response_authentication;
 	int     permit_empty_passwd;	/* If false, do not permit empty
 					 * passwords. */
 	int     permit_user_env;	/* If true, read ~/.ssh/environment */
+	int     permit_gid_override;	/* Allow gid to be overriden by PAM */
 	int     use_login;	/* If true, login(1) is used */
 	int     compression;	/* If true, compression is allowed */
 	int	allow_tcp_forwarding; /* One of FORWARD_* */
 	int	allow_streamlocal_forwarding; /* One of FORWARD_* */
 	int	allow_agent_forwarding;
 	u_int num_allow_users;
diff --git a/sshd.c b/sshd.c
index 6aa17fa..d1edfd0 100644
--- a/sshd.c
+++ b/sshd.c
@@ -2217,13 +2217,13 @@ main(int ac, char **av)
 		ssh_gssapi_storecreds();
 		restore_uid();
 	}
 #endif
 #ifdef USE_PAM
 	if (options.use_pam) {
-		do_pam_setcred(1);
+		do_pam_setcred(1, authctxt->pw);
 		do_pam_session();
 	}
 #endif
 
 	/*
 	 * In privilege separation, we fork another child and prepare
diff --git a/sshd_config b/sshd_config
index c9042ac..216e941 100644
--- a/sshd_config
+++ b/sshd_config
@@ -93,12 +93,15 @@ AuthorizedKeysFile	.ssh/authorized_keys
 # the setting of "PermitRootLogin without-password".
 # If you just want the PAM account and session checks to run without
 # PAM authentication, then enable this but set PasswordAuthentication
 # and ChallengeResponseAuthentication to 'no'.
 #UsePAM no
 
+# Allow PAM to override the process's gid
+#PermitGidOverride no
+
 #AllowAgentForwarding yes
 #AllowTcpForwarding yes
 #GatewayPorts no
 #X11Forwarding no
 #X11DisplayOffset 10
 #X11UseLocalhost yes
diff --git a/sshd_config.5 b/sshd_config.5
index 6dce0c7..52d0a52 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -990,12 +990,13 @@ Available keywords are
 .Cm KbdInteractiveAuthentication ,
 .Cm KerberosAuthentication ,
 .Cm MaxAuthTries ,
 .Cm MaxSessions ,
 .Cm PasswordAuthentication ,
 .Cm PermitEmptyPasswords ,
+.Cm PermitGidOverride ,
 .Cm PermitOpen ,
 .Cm PermitRootLogin ,
 .Cm PermitTTY ,
 .Cm PermitTunnel ,
 .Cm PermitUserRC ,
 .Cm PubkeyAcceptedKeyTypes ,
@@ -1050,12 +1051,15 @@ The default is
 .Dq yes .
 .It Cm PermitEmptyPasswords
 When password authentication is allowed, it specifies whether the
 server allows login to accounts with empty password strings.
 The default is
 .Dq no .
+.It Cm PermitGidOverride
+Allow PAM modules to override the gid of a process. The default is
+.Dq no .
 .It Cm PermitOpen
 Specifies the destinations to which TCP port forwarding is permitted.
 The forwarding specification must be one of the following forms:
 .Pp
 .Bl -item -offset indent -compact
 .It
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@xxxxxxxxxxx
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev

[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux