*/
/*
* Copyright (c) 2003,2004 Damien Miller <djm@mindrot.org>
- * Copyright (c) 2003,2004 Darren Tucker <dtucker@zip.com.au>
+ * Copyright (c) 2003,2004,2006 Darren Tucker <dtucker@zip.com.au>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
# 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)
{
static void
import_environments(Buffer *b)
{
- char *env;
+ char *env, *user;
u_int i, num_env;
int err;
/* 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);
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;
/* 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++)
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);
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",
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 */
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",
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.
-/* $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 <ylo@cs.hut.fi>
* 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 */
/* Portable-specific options */
options->use_pam = -1;
+ options->permit_pam_user_change = -1;
/* Standard Options */
options->num_ports = 0;
/* 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)
typedef enum {
sBadOption, /* == unknown option */
/* Portable-specific options */
- sUsePAM,
+ sUsePAM, sPermitPAMUserChange,
/* Standard Options */
sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime, sKeyRegenerationTime,
sPermitRootLogin, sLogFacility, sLogLevel,
/* 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 */
intptr = &options->use_pam;
goto parse_flag;
+ case sPermitPAMUserChange:
+ intptr = &options->permit_pam_user_change;
+ goto parse_flag;
+
/* Standard Options */
case sBadOption:
return -1;
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 */
# 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
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