From d037a8b0691afd8685d1a1202b3bf88c9542a8a9 Mon Sep 17 00:00:00 2001 From: basney Date: Wed, 19 Aug 2009 19:59:13 +0000 Subject: [PATCH] enable PAM user switching https://bugzilla.mcs.anl.gov/globus/show_bug.cgi?id=6839 --- openssh/auth-pam.c | 71 +++++++++++++++++++++++++++++++++++++-- openssh/auth-pam.h | 1 + openssh/auth.c | 4 +++ openssh/misc.c | 14 ++++++++ openssh/misc.h | 78 +++++++++++++++++++++++++++++++++++++------ openssh/servconf.c | 11 +++++- openssh/servconf.h | 1 + openssh/sshd_config | 4 +++ openssh/sshd_config.5 | 6 ++++ 9 files changed, 177 insertions(+), 13 deletions(-) diff --git a/openssh/auth-pam.c b/openssh/auth-pam.c index ccdb993..582c463 100644 --- a/openssh/auth-pam.c +++ b/openssh/auth-pam.c @@ -30,7 +30,7 @@ */ /* * Copyright (c) 2003,2004 Damien Miller - * Copyright (c) 2003,2004 Darren Tucker + * Copyright (c) 2003,2004,2006 Darren Tucker * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -272,6 +272,49 @@ sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags) # define pam_chauthtok(a,b) (sshpam_chauthtok_ruid((a), (b))) #endif +struct passwd * +sshpam_getpw(const char *user) +{ + struct passwd *pw; + + if ((pw = getpwnam(user)) != NULL) + return(pw); + + debug("PAM: faking passwd struct for user '%.100s'", user); + if ((pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) + return NULL; + pw->pw_name = xstrdup(user); /* XXX leak */ + pw->pw_shell = "/bin/true"; + pw->pw_gecos = "sshd fake PAM user"; + return (pw); +} + +void +sshpam_check_userchanged(void) +{ + int sshpam_err; + struct passwd *pw; + const char *user; + + debug("sshpam_check_userchanged"); + sshpam_err = pam_get_item(sshpam_handle, PAM_USER, &user); + if (sshpam_err != PAM_SUCCESS) + fatal("PAM: could not get PAM_USER: %s", + pam_strerror(sshpam_handle, sshpam_err)); + if (strcmp(user, sshpam_authctxt->pw->pw_name) != 0) { + debug("PAM: user mapped from '%.100s' to '%.100s'", + sshpam_authctxt->pw->pw_name, user); + if ((pw = getpwnam(user)) == NULL) + fatal("PAM: could not get passwd entry for user " + "'%.100s' provided by PAM_USER", user); + pwfree(sshpam_authctxt->pw); + sshpam_authctxt->pw = pw; + sshpam_authctxt->valid = allowed_user(pw); + debug("PAM: user '%.100s' now %svalid", user, + sshpam_authctxt->valid ? "" : "in"); + } +} + void sshpam_password_change_required(int reqd) { @@ -294,7 +337,7 @@ sshpam_password_change_required(int reqd) static void import_environments(Buffer *b) { - char *env; + char *env, *user; u_int i, num_env; int err; @@ -304,6 +347,15 @@ import_environments(Buffer *b) /* Import variables set by do_pam_account */ sshpam_account_status = buffer_get_int(b); sshpam_password_change_required(buffer_get_int(b)); + if (options.permit_pam_user_change) { + user = buffer_get_string(b, NULL); + debug("PAM: got username '%.100s' from thread", user); + if ((err = pam_set_item(sshpam_handle, PAM_USER, user)) != PAM_SUCCESS) + fatal("PAM: failed to set PAM_USER: %s", + pam_strerror(sshpam_handle, err)); + pwfree(sshpam_authctxt->pw); + sshpam_authctxt->pw = pwcopy(sshpam_getpw(user)); + } /* Import environment from subprocess */ num_env = buffer_get_int(b); @@ -469,6 +521,9 @@ sshpam_thread(void *ctxtp) if (sshpam_err != PAM_SUCCESS) goto auth_fail; + if (options.permit_pam_user_change) { + sshpam_check_userchanged(); + } if (compat20) { if (!do_pam_account()) { sshpam_err = PAM_ACCT_EXPIRED; @@ -489,6 +544,9 @@ sshpam_thread(void *ctxtp) /* Export variables set by do_pam_account */ buffer_put_int(&buffer, sshpam_account_status); buffer_put_int(&buffer, sshpam_authctxt->force_pwchange); + if (options.permit_pam_user_change) { + buffer_put_cstring(&buffer, sshpam_authctxt->pw->pw_name); + } /* Export any environment strings set in child */ for(i = 0; environ[i] != NULL; i++) @@ -907,6 +965,12 @@ do_pam_account(void) debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err, pam_strerror(sshpam_handle, sshpam_err)); + if (options.permit_pam_user_change) { + sshpam_check_userchanged(); + if (getpwnam(sshpam_authctxt->pw->pw_name) == NULL) + fatal("PAM: completed authentication but PAM account invalid"); + } + if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) { sshpam_account_status = 0; return (sshpam_account_status); @@ -1206,6 +1270,9 @@ sshpam_auth_passwd(Authctxt *authctxt, const char *password) pam_strerror(sshpam_handle, sshpam_err)); sshpam_err = pam_authenticate(sshpam_handle, flags); + if (options.permit_pam_user_change) { + sshpam_check_userchanged(); + } sshpam_password = NULL; if (sshpam_err == PAM_SUCCESS && authctxt->valid) { debug("PAM: password authentication accepted for %.100s", diff --git a/openssh/auth-pam.h b/openssh/auth-pam.h index 93a1eca..c2cb6dc 100644 --- a/openssh/auth-pam.h +++ b/openssh/auth-pam.h @@ -46,5 +46,6 @@ void sshpam_thread_cleanup(void); void sshpam_cleanup(void); int sshpam_auth_passwd(Authctxt *, const char *); int is_pam_session_open(void); +struct passwd *sshpam_getpw(const char *); #endif /* USE_PAM */ diff --git a/openssh/auth.c b/openssh/auth.c index 2106dfc..010e550 100644 --- a/openssh/auth.c +++ b/openssh/auth.c @@ -527,6 +527,10 @@ getpwnamallow(const char *user) get_canonical_hostname(options.use_dns), get_remote_ipaddr()); pw = getpwnam(user); +#ifdef USE_PAM + if (options.use_pam && options.permit_pam_user_change && pw == NULL) + pw = sshpam_getpw(user); +#endif if (pw == NULL) { logit("Invalid user %.100s from %.100s", (user && user[0]) ? user : "unknown", diff --git a/openssh/misc.c b/openssh/misc.c index 83efded..c447d23 100644 --- a/openssh/misc.c +++ b/openssh/misc.c @@ -237,6 +237,20 @@ pwcopy(struct passwd *pw) return copy; } +void +pwfree(struct passwd *pw) +{ + xfree(pw->pw_name); + xfree(pw->pw_passwd); + xfree(pw->pw_gecos); +#ifdef HAVE_PW_CLASS_IN_PASSWD + xfree(pw->pw_class); +#endif + xfree(pw->pw_dir); + xfree(pw->pw_shell); + xfree(pw); +} + /* * Convert ASCII string to TCP/IP port number. * Port must be >=0 and <=65535. diff --git a/openssh/misc.h b/openssh/misc.h index fc56452..b7299e3 100644 --- a/openssh/misc.h +++ b/openssh/misc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.h,v 1.10 2001/06/26 17:27:24 markus Exp $ */ +/* $OpenBSD: misc.h,v 1.38 2008/06/12 20:38:28 dtucker Exp $ */ /* * Author: Tatu Ylonen @@ -12,25 +12,83 @@ * called by a name other than "ssh" or "Secure Shell". */ +#ifndef _MISC_H +#define _MISC_H + +/* misc.c */ + char *chop(char *); char *strdelim(char **); -void set_nonblock(int); -void unset_nonblock(int); +int set_nonblock(int); +int unset_nonblock(int); +void set_nodelay(int); int a2port(const char *); +int a2tun(const char *, int *); +char *put_host_port(const char *, u_short); +char *hpdelim(char **); char *cleanhostname(char *); char *colon(char *); long convtime(const char *); +char *tilde_expand_filename(const char *, uid_t); +char *percent_expand(const char *, ...) __attribute__((__sentinel__)); +char *tohex(const void *, size_t); +void sanitise_stdfd(void); +void ms_subtract_diff(struct timeval *, int *); +void ms_to_timeval(struct timeval *, int); struct passwd *pwcopy(struct passwd *); +void pwfree(struct passwd *); +const char *ssh_gai_strerror(int); typedef struct arglist arglist; struct arglist { - char **list; - int num; - int nalloc; + char **list; + u_int num; + u_int nalloc; }; -void addargs(arglist *, char *, ...) __attribute__((format(printf, 2, 3))); +void addargs(arglist *, char *, ...) + __attribute__((format(printf, 2, 3))); +void replacearg(arglist *, u_int, char *, ...) + __attribute__((format(printf, 3, 4))); +void freeargs(arglist *); + +int tun_open(int, int); + +/* Common definitions for ssh tunnel device forwarding */ +#define SSH_TUNMODE_NO 0x00 +#define SSH_TUNMODE_POINTOPOINT 0x01 +#define SSH_TUNMODE_ETHERNET 0x02 +#define SSH_TUNMODE_DEFAULT SSH_TUNMODE_POINTOPOINT +#define SSH_TUNMODE_YES (SSH_TUNMODE_POINTOPOINT|SSH_TUNMODE_ETHERNET) + +#define SSH_TUNID_ANY 0x7fffffff +#define SSH_TUNID_ERR (SSH_TUNID_ANY - 1) +#define SSH_TUNID_MAX (SSH_TUNID_ANY - 2) + +/* Functions to extract or store big-endian words of various sizes */ +u_int64_t get_u64(const void *) + __attribute__((__bounded__( __minbytes__, 1, 8))); +u_int32_t get_u32(const void *) + __attribute__((__bounded__( __minbytes__, 1, 4))); +u_int16_t get_u16(const void *) + __attribute__((__bounded__( __minbytes__, 1, 2))); +void put_u64(void *, u_int64_t) + __attribute__((__bounded__( __minbytes__, 1, 8))); +void put_u32(void *, u_int32_t) + __attribute__((__bounded__( __minbytes__, 1, 4))); +void put_u16(void *, u_int16_t) + __attribute__((__bounded__( __minbytes__, 1, 2))); + + +/* readpass.c */ + +#define RP_ECHO 0x0001 +#define RP_ALLOW_STDIN 0x0002 +#define RP_ALLOW_EOF 0x0004 +#define RP_USE_ASKPASS 0x0008 + +char *read_passphrase(const char *, int); +int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2))); +int read_keyfile_line(FILE *, const char *, char *, size_t, u_long *); -/* wrapper for signal interface */ -typedef void (*mysig_t)(int); -mysig_t mysignal(int sig, mysig_t act); +#endif /* _MISC_H */ diff --git a/openssh/servconf.c b/openssh/servconf.c index a693e05..90c0eb7 100644 --- a/openssh/servconf.c +++ b/openssh/servconf.c @@ -58,6 +58,7 @@ initialize_server_options(ServerOptions *options) /* Portable-specific options */ options->use_pam = -1; + options->permit_pam_user_change = -1; /* Standard Options */ options->num_ports = 0; @@ -154,6 +155,8 @@ fill_default_server_options(ServerOptions *options) /* Portable-specific options */ if (options->use_pam == -1) options->use_pam = 0; + if (options->permit_pam_user_change == -1) + options->permit_pam_user_change = 0; /* Standard Options */ if (options->protocol == SSH_PROTO_UNKNOWN) @@ -343,7 +346,7 @@ fill_default_server_options(ServerOptions *options) typedef enum { sBadOption, /* == unknown option */ /* Portable-specific options */ - sUsePAM, + sUsePAM, sPermitPAMUserChange, /* Standard Options */ sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime, sKeyRegenerationTime, sPermitRootLogin, sLogFacility, sLogLevel, @@ -394,8 +397,10 @@ static struct { /* Portable-specific options */ #ifdef USE_PAM { "usepam", sUsePAM, SSHCFG_GLOBAL }, + { "permitpamuserchange", sPermitPAMUserChange, SSHCFG_GLOBAL } #else { "usepam", sUnsupported, SSHCFG_GLOBAL }, + { "permitpamuserchange", sUnsupported, SSHCFG_GLOBAL }, #endif { "pamauthenticationviakbdint", sDeprecated, SSHCFG_GLOBAL }, /* Standard Options */ @@ -777,6 +782,10 @@ process_server_config_line(ServerOptions *options, char *line, intptr = &options->use_pam; goto parse_flag; + case sPermitPAMUserChange: + intptr = &options->permit_pam_user_change; + goto parse_flag; + /* Standard Options */ case sBadOption: return -1; diff --git a/openssh/servconf.h b/openssh/servconf.h index 6ca0980..fd8b909 100644 --- a/openssh/servconf.h +++ b/openssh/servconf.h @@ -155,6 +155,7 @@ typedef struct { char *adm_forced_command; int use_pam; /* Enable auth via PAM */ + int permit_pam_user_change; /* Allow PAM to change user name */ int none_enabled; /* enable NONE cipher switch */ int tcp_rcv_buf_poll; /* poll tcp rcv window in autotuning kernels*/ int hpn_disabled; /* disable hpn functionality. false by default */ diff --git a/openssh/sshd_config b/openssh/sshd_config index cf5d1d0..f1f4a10 100644 --- a/openssh/sshd_config +++ b/openssh/sshd_config @@ -93,6 +93,10 @@ Protocol 2 # and ChallengeResponseAuthentication to 'no'. #UsePAM no +# Set to 'yes' to allow the PAM stack to change the user name during +# calls to authentication +#PermitPAMUserChange no + #AllowAgentForwarding yes #AllowTcpForwarding yes #GatewayPorts no diff --git a/openssh/sshd_config.5 b/openssh/sshd_config.5 index 6649d32..414be24 100644 --- a/openssh/sshd_config.5 +++ b/openssh/sshd_config.5 @@ -966,6 +966,12 @@ is enabled, you will not be able to run as a non-root user. The default is .Dq no . +.It Cm PermitPAMUserChange +If set to +.Dq yes +this will enable PAM authentication to change the name of the user being +authenticated. The default is +.Dq no . .It Cm UsePrivilegeSeparation Specifies whether .Xr sshd 8 -- 2.45.1