1 /*- 2 * Copyright (c) 1999 Andrew J. Korty 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 * 28 */ 29 30 31 #include <sys/param.h> 32 33 #include <fcntl.h> 34 #include <paths.h> 35 #include <pwd.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 41 #define PAM_SM_AUTH 42 #define PAM_SM_SESSION 43 #include <security/pam_modules.h> 44 #include <security/pam_mod_misc.h> 45 46 #include "includes.h" 47 #include "rsa.h" 48 #include "ssh.h" 49 #include "authfd.h" 50 51 #define MODULE_NAME "pam_ssh" 52 #define NEED_PASSPHRASE "Need passphrase for %s (%s).\nEnter passphrase: " 53 #define PATH_SSH_AGENT "__PREFIX__/bin/ssh-agent" 54 55 56 void 57 rsa_cleanup(pam_handle_t *pamh, void *data, int error_status) 58 { 59 if (data) 60 RSA_free(data); 61 } 62 63 64 void 65 ssh_cleanup(pam_handle_t *pamh, void *data, int error_status) 66 { 67 if (data) 68 free(data); 69 } 70 71 72 typedef struct passwd PASSWD; 73 74 PAM_EXTERN int 75 pam_sm_authenticate( 76 pam_handle_t *pamh, 77 int flags, 78 int argc, 79 const char **argv) 80 { 81 char *comment_priv; /* on private key */ 82 char *comment_pub; /* on public key */ 83 char *identity; /* user's identity file */ 84 RSA *key; /* user's private key */ 85 int options; /* module options */ 86 const char *pass; /* passphrase */ 87 char *prompt; /* passphrase prompt */ 88 RSA *public_key; /* user's public key */ 89 const PASSWD *pwent; /* user's passwd entry */ 90 PASSWD *pwent_keep; /* our own copy */ 91 int retval; /* from calls */ 92 uid_t saved_uid; /* caller's uid */ 93 const char *user; /* username */ 94 95 options = 0; 96 while (argc--) 97 pam_std_option(&options, *argv++); 98 if ((retval = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS) 99 return retval; 100 if (!((pwent = getpwnam(user)) && pwent->pw_dir)) { 101 /* delay? */ 102 return PAM_AUTH_ERR; 103 } 104 /* locate the user's private key file */ 105 if (!asprintf(&identity, "%s/%s", pwent->pw_dir, 106 SSH_CLIENT_IDENTITY)) { 107 syslog(LOG_CRIT, "%s: %m", MODULE_NAME); 108 return PAM_SERVICE_ERR; 109 } 110 /* 111 * Fail unless we can load the public key. Change to the 112 * owner's UID to appease load_public_key(). 113 */ 114 key = RSA_new(); 115 public_key = RSA_new(); 116 saved_uid = getuid(); 117 (void)setreuid(pwent->pw_uid, saved_uid); 118 retval = load_public_key(identity, public_key, &comment_pub); 119 (void)setuid(saved_uid); 120 if (!retval) { 121 free(identity); 122 return PAM_AUTH_ERR; 123 } 124 RSA_free(public_key); 125 /* build the passphrase prompt */ 126 retval = asprintf(&prompt, NEED_PASSPHRASE, identity, comment_pub); 127 free(comment_pub); 128 if (!retval) { 129 syslog(LOG_CRIT, "%s: %m", MODULE_NAME); 130 free(identity); 131 return PAM_SERVICE_ERR; 132 } 133 /* pass prompt message to application and receive passphrase */ 134 retval = pam_get_pass(pamh, &pass, prompt, options); 135 free(prompt); 136 if (retval != PAM_SUCCESS) { 137 free(identity); 138 return retval; 139 } 140 /* 141 * Try to decrypt the private key with the passphrase provided. 142 * If success, the user is authenticated. 143 */ 144 (void)setreuid(pwent->pw_uid, saved_uid); 145 retval = load_private_key(identity, pass, key, &comment_priv); 146 free(identity); 147 (void)setuid(saved_uid); 148 if (!retval) 149 return PAM_AUTH_ERR; 150 /* 151 * Save the key and comment to pass to ssh-agent in the session 152 * phase. 153 */ 154 if ((retval = pam_set_data(pamh, "ssh_private_key", key, 155 rsa_cleanup)) != PAM_SUCCESS) { 156 RSA_free(key); 157 free(comment_priv); 158 return retval; 159 } 160 if ((retval = pam_set_data(pamh, "ssh_key_comment", comment_priv, 161 ssh_cleanup)) != PAM_SUCCESS) { 162 free(comment_priv); 163 return retval; 164 } 165 /* 166 * Copy the passwd entry (in case successive calls are made) 167 * and save it for the session phase. 168 */ 169 if (!(pwent_keep = malloc(sizeof *pwent))) { 170 syslog(LOG_CRIT, "%m"); 171 return PAM_SERVICE_ERR; 172 } 173 (void)memcpy(pwent_keep, pwent, sizeof *pwent_keep); 174 if ((retval = pam_set_data(pamh, "ssh_passwd_entry", pwent_keep, 175 ssh_cleanup)) != PAM_SUCCESS) { 176 free(pwent_keep); 177 return retval; 178 } 179 return PAM_SUCCESS; 180 } 181 182 183 PAM_EXTERN int 184 pam_sm_setcred( 185 pam_handle_t *pamh, 186 int flags, 187 int argc, 188 const char **argv) 189 { 190 return PAM_SUCCESS; 191 } 192 193 194 typedef AuthenticationConnection AC; 195 196 PAM_EXTERN int 197 pam_sm_open_session( 198 pam_handle_t *pamh, 199 int flags, 200 int argc, 201 const char **argv) 202 { 203 AC *ac; /* to ssh-agent */ 204 char *comment; /* on private key */ 205 char *env_end; /* end of env */ 206 char *env_file; /* to store env */ 207 FILE *env_fp; /* env_file handle */ 208 RSA *key; /* user's private key */ 209 FILE *pipe; /* ssh-agent handle */ 210 const PASSWD *pwent; /* user's passwd entry */ 211 int retval; /* from calls */ 212 uid_t saved_uid; /* caller's uid */ 213 const char *tty; /* tty or display name */ 214 char hname[MAXHOSTNAMELEN]; /* local hostname */ 215 char parse[BUFSIZ]; /* commands output */ 216 217 /* dump output of ssh-agent in ~/.ssh */ 218 if ((retval = pam_get_data(pamh, "ssh_passwd_entry", 219 (const void **)&pwent)) != PAM_SUCCESS) 220 return retval; 221 /* use the tty or X display name in the filename */ 222 if ((retval = pam_get_item(pamh, PAM_TTY, (const void **)&tty)) 223 != PAM_SUCCESS) 224 return retval; 225 if (*tty == ':' && gethostname(hname, sizeof hname) == 0) { 226 if (asprintf(&env_file, "%s/.ssh/agent-%s%s", 227 pwent->pw_dir, hname, tty) == -1) { 228 syslog(LOG_CRIT, "%s: %m", MODULE_NAME); 229 return PAM_SERVICE_ERR; 230 } 231 } else if (asprintf(&env_file, "%s/.ssh/agent-%s", pwent->pw_dir, 232 tty) == -1) { 233 syslog(LOG_CRIT, "%s: %m", MODULE_NAME); 234 return PAM_SERVICE_ERR; 235 } 236 /* save the filename so we can delete the file on session close */ 237 if ((retval = pam_set_data(pamh, "ssh_agent_env", env_file, 238 ssh_cleanup)) != PAM_SUCCESS) { 239 free(env_file); 240 return retval; 241 } 242 /* start the agent as the user */ 243 saved_uid = geteuid(); 244 (void)seteuid(pwent->pw_uid); 245 env_fp = fopen(env_file, "w"); 246 pipe = popen(PATH_SSH_AGENT, "r"); 247 (void)seteuid(saved_uid); 248 if (!pipe) { 249 syslog(LOG_ERR, "%s: %s: %m", MODULE_NAME, PATH_SSH_AGENT); 250 if (env_fp) 251 (void)fclose(env_fp); 252 return PAM_SESSION_ERR; 253 } 254 while (fgets(parse, sizeof parse, pipe)) { 255 if (env_fp) 256 (void)fputs(parse, env_fp); 257 /* 258 * Save environment for application with pam_putenv() 259 * but also with putenv() for our own call to 260 * ssh_get_authentication_connection(). 261 */ 262 if (strchr(parse, '=') && (env_end = strchr(parse, ';'))) { 263 *env_end = '\0'; 264 /* pass to the application ... */ 265 if (!((retval = pam_putenv(pamh, parse)) == 266 PAM_SUCCESS && putenv(parse) == 0)) { 267 (void)pclose(pipe); 268 if (env_fp) 269 (void)fclose(env_fp); 270 return PAM_SERVICE_ERR; 271 } 272 } 273 } 274 if (env_fp) 275 (void)fclose(env_fp); 276 retval = pclose(pipe); 277 if (retval > 0) { 278 syslog(LOG_ERR, "%s: %s exited with status %d", 279 MODULE_NAME, PATH_SSH_AGENT, WEXITSTATUS(retval)); 280 return PAM_SESSION_ERR; 281 } else if (retval < 0) { 282 syslog(LOG_ERR, "%s: %s: %m", MODULE_NAME, PATH_SSH_AGENT); 283 return PAM_SESSION_ERR; 284 } 285 /* connect to the agent and hand off the private key */ 286 if ((retval = pam_get_data(pamh, "ssh_private_key", 287 (const void **)&key)) != PAM_SUCCESS) 288 return retval; 289 if ((retval = pam_get_data(pamh, "ssh_key_comment", 290 (const void **)&comment)) != PAM_SUCCESS) 291 return retval; 292 if (!(ac = ssh_get_authentication_connection())) { 293 syslog(LOG_ERR, "%s: could not connect to agent", 294 MODULE_NAME); 295 return PAM_SESSION_ERR; 296 } 297 retval = ssh_add_identity(ac, key, comment); 298 ssh_close_authentication_connection(ac); 299 return retval ? PAM_SUCCESS : PAM_SESSION_ERR; 300 } 301 302 303 PAM_EXTERN int 304 pam_sm_close_session( 305 pam_handle_t *pamh, 306 int flags, 307 int argc, 308 const char **argv) 309 { 310 const char *env_file; /* ssh-agent environment */ 311 int retval; /* from calls */ 312 313 /* kill the agent */ 314 if ((retval = system(PATH_SSH_AGENT " -k")) != 0) { 315 syslog(LOG_ERR, "%s: %s -k exited with status %d", 316 MODULE_NAME, PATH_SSH_AGENT, WEXITSTATUS(retval)); 317 return PAM_SESSION_ERR; 318 } 319 /* retrieve environment filename, then remove the file */ 320 if ((retval = pam_get_data(pamh, "ssh_agent_env", 321 (const void **)&env_file)) != PAM_SUCCESS) 322 return retval; 323 (void)unlink(env_file); 324 return PAM_SUCCESS; 325 } 326 327 328 PAM_MODULE_ENTRY(MODULE_NAME); 329