pam_ssh: Don't allow a bogus passphrase for unencrypted keys.
authorPeter Avalos <pavalos@dragonflybsd.org>
Sat, 24 Dec 2011 21:00:13 +0000 (13:00 -0800)
committerPeter Avalos <pavalos@dragonflybsd.org>
Sat, 24 Dec 2011 21:00:13 +0000 (13:00 -0800)
key_load_private() ignores the passphrase argument if the private key
is unencrypted.  This defeats the nullok check, because it means a
non-null passphrase will successfully unlock the key.

To address this, try at first to load the key without a passphrase.
If this succeeds and the user provided a non-empty passphrase *or*
nullok is false, reject the key.

While I'm here: Load the ECDSA key if there is one.

Obtained-from:  FreeBSD 227757, 219426, & 226101

lib/pam_module/pam_ssh/pam_ssh.8
lib/pam_module/pam_ssh/pam_ssh.c

index 1e8a402..3ba9e66 100644 (file)
@@ -1,6 +1,6 @@
 .\" Copyright (c) 2001 Mark R V Murray
-.\" All rights reserved.
 .\" Copyright (c) 2001-2003 Networks Associates Technology, Inc.
+.\" Copyright (c) 2004-2011 Dag-Erling Smørgrav
 .\" All rights reserved.
 .\"
 .\" This software was developed for the FreeBSD Project by ThinkSec AS and
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libpam/modules/pam_ssh/pam_ssh.8,v 1.14 2005/09/22 05:35:24 des Exp $
-.\" $DragonFly: src/lib/pam_module/pam_ssh/pam_ssh.8,v 1.1 2005/07/12 23:26:49 joerg Exp $
+.\" $FreeBSD: src/lib/libpam/modules/pam_ssh/pam_ssh.8,v 1.15 2011/10/07 12:58:33 des Exp $
 .\"
-.Dd November 26, 2001
+.Dd December 24, 2011
 .Dt PAM_SSH 8
 .Os
 .Sh NAME
@@ -136,6 +135,8 @@ SSH1 RSA key
 SSH2 RSA key
 .It Pa $HOME/.ssh/id_dsa
 SSH2 DSA key
+.It Pa $HOME/.ssh/id_ecdsa
+SSH2 ECDSA key
 .El
 .Sh SEE ALSO
 .Xr ssh-agent 1 ,
index 2f69c6f..954fb32 100644 (file)
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003 Networks Associates Technology, Inc.
+ * Copyright (c) 2004-2011 Dag-Erling Smørgrav
  * All rights reserved.
  *
  * This software was developed for the FreeBSD Project by ThinkSec AS and
@@ -31,7 +32,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libpam/modules/pam_ssh/pam_ssh.c,v 1.45 2007/12/21 12:00:15 des Exp $
+ * $FreeBSD: src/lib/libpam/modules/pam_ssh/pam_ssh.c,v 1.49 2011/11/20 15:18:49 des Exp $
  */
 
 #include <sys/param.h>
@@ -77,6 +78,7 @@ static const char *pam_ssh_keyfiles[] = {
        ".ssh/identity",        /* SSH1 RSA key */
        ".ssh/id_rsa",          /* SSH2 RSA key */
        ".ssh/id_dsa",          /* SSH2 DSA key */
+       ".ssh/id_ecdsa",        /* SSH2 ECDSA key */
        NULL
 };
 
@@ -90,7 +92,8 @@ static char *const pam_ssh_agent_envp[] = { NULL };
  * struct pam_ssh_key containing the key and its comment.
  */
 static struct pam_ssh_key *
-pam_ssh_load_key(const char *dir, const char *kfn, const char *passphrase)
+pam_ssh_load_key(const char *dir, const char *kfn, const char *passphrase,
+    int nullok)
 {
        struct pam_ssh_key *psk;
        char fn[PATH_MAX];
@@ -100,13 +103,27 @@ pam_ssh_load_key(const char *dir, const char *kfn, const char *passphrase)
        if (snprintf(fn, sizeof(fn), "%s/%s", dir, kfn) > (int)sizeof(fn))
                return (NULL);
        comment = NULL;
-       key = key_load_private(fn, passphrase, &comment);
+       /*
+        * If the key is unencrypted, OpenSSL ignores the passphrase, so
+        * it will seem like the user typed in the right one.  This allows
+        * a user to circumvent nullok by providing a dummy passphrase.
+        * Verify that the key really *is* encrypted by trying to load it
+        * with an empty passphrase, and if the key is not encrypted,
+        * accept only an empty passphrase.
+        */
+       key = key_load_private(fn, NULL, &comment);
+       if (key != NULL && !(*passphrase == '\0' && nullok)) {
+               key_free(key);
+               return (NULL);
+       }
+       if (key == NULL)
+               key = key_load_private(fn, passphrase, &comment);
        if (key == NULL) {
-               openpam_log(PAM_LOG_DEBUG, "failed to load key from %s\n", fn);
+               openpam_log(PAM_LOG_DEBUG, "failed to load key from %s", fn);
                return (NULL);
        }
 
-       openpam_log(PAM_LOG_DEBUG, "loaded '%s' from %s\n", comment, fn);
+       openpam_log(PAM_LOG_DEBUG, "loaded '%s' from %s", comment, fn);
        if ((psk = malloc(sizeof(*psk))) == NULL) {
                key_free(key);
                free(comment);
@@ -167,9 +184,6 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags __unused,
        if (pam_err != PAM_SUCCESS)
                return (pam_err);
 
-       if (*passphrase == '\0' && !nullok)
-               goto skip_keys;
-
        /* switch to user credentials */
        pam_err = openpam_borrow_cred(pamh, pwd);
        if (pam_err != PAM_SUCCESS)
@@ -177,7 +191,7 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags __unused,
 
        /* try to load keys from all keyfiles we know of */
        for (kfn = pam_ssh_keyfiles; *kfn != NULL; ++kfn) {
-               psk = pam_ssh_load_key(pwd->pw_dir, *kfn, passphrase);
+               psk = pam_ssh_load_key(pwd->pw_dir, *kfn, passphrase, nullok);
                if (psk != NULL) {
                        pam_set_data(pamh, *kfn, psk, pam_ssh_free_key);
                        ++nkeys;
@@ -187,7 +201,6 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags __unused,
        /* switch back to arbitrator credentials */
        openpam_restore_cred(pamh);
 
- skip_keys:
        /*
         * If we tried an old token and didn't get anything, and
         * try_first_pass was specified, try again after prompting the
@@ -312,6 +325,7 @@ pam_ssh_add_keys_to_agent(pam_handle_t *pamh)
        AuthenticationConnection *ac;
        const struct pam_ssh_key *psk;
        const char **kfn;
+       const void *item;
        char **envlist, **env;
        int pam_err;
 
@@ -324,16 +338,16 @@ pam_ssh_add_keys_to_agent(pam_handle_t *pamh)
 
        /* get a connection to the agent */
        if ((ac = ssh_get_authentication_connection()) == NULL) {
+               openpam_log(PAM_LOG_DEBUG, "failed to connect to the agent");
                pam_err = PAM_SYSTEM_ERR;
                goto end;
        }
 
        /* look for keys to add to it */
        for (kfn = pam_ssh_keyfiles; *kfn != NULL; ++kfn) {
-               const void *vp;
-               pam_err = pam_get_data(pamh, *kfn, &vp);
-               psk = vp;
-               if (pam_err == PAM_SUCCESS && psk != NULL) {
+               pam_err = pam_get_data(pamh, *kfn, &item);
+               if (pam_err == PAM_SUCCESS && item != NULL) {
+                       psk = item;
                        if (ssh_add_identity(ac, psk->key, psk->comment))
                                openpam_log(PAM_LOG_DEBUG,
                                    "added %s to ssh agent", psk->comment);