[PATCH] Expose authentication information

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

 



Hi all,

Following a discussion of last June and the resulting bug (2408), in
order to improve 2 factor authentication, I've worked on a set of
patches to expose at least basic authentication information and more
when possible. By exposing such information to PAM, it would be possible
to differentiate the cases when PAM is called first and when it is
called after a valid authentication (e.g. when using
AuthenticationMethods gssapi-with-mic,keyboard-interactive:pam
publickey,keyboard-interactive:pam keyboard-interactive:pam)

The design of these patches is rather simple:
- Authentication method can "publish" detailed information about the
  client, if the authentication was successful
- In the main auth 2 loop, after a successful authentication, the
  authentication method is recorded, including details if provided
- When calling PAM or a shell, export this information via an
  environment variable

I've tried to keep the patches as small and atomic as possible.
The larger change is probably the introduction of 'pubkey_format' in
order to only have one function to print a public key.

This set of patch could be extended to cover more ground, but I not sure
how and thus I'm open to suggestions and ideas on how:
- to expose the same kind of detailed information from the priviledged
  thread in case of priviledge separation
- to produce the same kind of information from other authentication
  methods (I'm particularly interested in getting the kerbors principal
  from gss-serv-krb5 for example)

Resolving such questions would probably make this feature complete, but
I fear that they need complex modifications of the existing code. Do you
think that this design and this first step could be considered and
merged first, without closing the door for further improvement?

Thanks in advance,
Vincent

PS: I've posted these patches as a single patch on the bugzilla, but as
I didn't get any feedback, I'm trying my luck here directly
>From 99f2f709afedceed0cc14235f46e90081e4aa432 Mon Sep 17 00:00:00 2001
From: Vincent Brillault <git@xxxxxxxxx>
Date: Wed, 18 Nov 2015 20:01:30 +0100
Subject: [PATCH 1/8] Store details about successful auth methods

---
 auth.h    |  3 +++
 auth2.c   | 13 +++++++++++++
 session.c |  3 +++
 3 files changed, 19 insertions(+)

diff --git a/auth.h b/auth.h
index fffbe6c..391c0a7 100644
--- a/auth.h
+++ b/auth.h
@@ -81,6 +81,9 @@ struct Authctxt {
 
 	struct sshkey	**prev_userkeys;
 	u_int		 nprev_userkeys;
+
+	char		*last_details;
+	char		*auth_details;
 };
 /*
  * Every authentication method has to handle authentication requests for
diff --git a/auth2.c b/auth2.c
index 7177962..99eb750 100644
--- a/auth2.c
+++ b/auth2.c
@@ -293,6 +293,7 @@ userauth_finish(Authctxt *authctxt, int authenticated, const char *method,
     const char *submethod)
 {
 	char *methods;
+	char *prev_auth_details;
 	int partial = 0;
 
 	if (!authctxt->valid && authenticated)
@@ -323,6 +324,18 @@ userauth_finish(Authctxt *authctxt, int authenticated, const char *method,
 	if (authctxt->postponed)
 		return;
 
+	if (authenticated || partial) {
+		prev_auth_details = authctxt->auth_details;
+		xasprintf(&authctxt->auth_details, "%s%s%s%s%s",
+		    prev_auth_details ? prev_auth_details : "",
+		    prev_auth_details ? ", " : "", method,
+		    authctxt->last_details ? ": " : "",
+		    authctxt->last_details ? authctxt->last_details : "");
+		free(authctxt->last_details);
+		authctxt->last_details = NULL;
+		free(prev_auth_details);
+	}
+
 #ifdef USE_PAM
 	if (options.use_pam && authenticated) {
 		if (!PRIVSEP(do_pam_account())) {
diff --git a/session.c b/session.c
index 26f4742..c1f5ab2 100644
--- a/session.c
+++ b/session.c
@@ -2735,6 +2735,9 @@ do_cleanup(Authctxt *authctxt)
 	if (authctxt == NULL)
 		return;
 
+	free(authctxt->auth_details);
+	authctxt->auth_details = NULL;
+
 #ifdef USE_PAM
 	if (options.use_pam) {
 		sshpam_cleanup();
-- 
2.4.10

>From bb0c1ccd71f1c52e926d7b6ad726da71efb7518c Mon Sep 17 00:00:00 2001
From: Vincent Brillault <git@xxxxxxxxx>
Date: Thu, 19 Nov 2015 20:26:26 +0100
Subject: [PATCH 2/8] Export auth_details to child env as SSH_USER_AUTH

---
 session.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/session.c b/session.c
index c1f5ab2..a54dbe0 100644
--- a/session.c
+++ b/session.c
@@ -1307,6 +1307,10 @@ do_setup_env(Session *s, const char *shell)
 	}
 #endif /* USE_PAM */
 
+	if (s->authctxt->auth_details)
+		child_set_env(&env, &envsize, "SSH_USER_AUTH",
+		     s->authctxt->auth_details);
+
 	if (auth_sock_name != NULL)
 		child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME,
 		    auth_sock_name);
-- 
2.4.10

>From 807787f8e09f703305074a8c5e0abb4eb5777e3a Mon Sep 17 00:00:00 2001
From: Vincent Brillault <git@xxxxxxxxx>
Date: Thu, 19 Nov 2015 20:26:53 +0100
Subject: [PATCH 3/8] Expose auth_details to pam via "SSH_USER_AUTH"

Whenever the pam device is called, update the "SSH_USER_AUTH" PAM
environment variable (Doing it outside this module exposes us to a NULL
sshpam_handle).

When a session will be later created, this variable, still part of the
PAM environment variable will be copied to the child environment before
being overriden by the latest value stored in auth_details. As a result,
using the same variable name as the final one is key to prevent the
final environment to be poluted with an outdated value.
---
 auth-pam.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/auth-pam.c b/auth-pam.c
index d94c828..815c833 100644
--- a/auth-pam.c
+++ b/auth-pam.c
@@ -688,6 +688,10 @@ sshpam_init_ctx(Authctxt *authctxt)
 		return (NULL);
 	}
 
+	/* Notify PAM about any already successful auth methods */
+	if (authctxt->auth_details)
+		do_pam_putenv("SSH_USER_AUTH", authctxt->auth_details);
+
 	ctxt = xcalloc(1, sizeof *ctxt);
 
 	/* Start the authentication thread */
-- 
2.4.10

>From c087cdfe4a7e6cf752a0fff4830eb32b4991b8b3 Mon Sep 17 00:00:00 2001
From: Vincent Brillault <git@xxxxxxxxx>
Date: Wed, 18 Nov 2015 21:11:07 +0100
Subject: [PATCH 4/8] Extract pubkey_format from pubkey_auth_info

---
 auth.h         |  1 +
 auth2-pubkey.c | 50 ++++++++++++++++++++++++++++++--------------------
 2 files changed, 31 insertions(+), 20 deletions(-)

diff --git a/auth.h b/auth.h
index 391c0a7..8a55f72 100644
--- a/auth.h
+++ b/auth.h
@@ -130,6 +130,7 @@ int	 auth_rsa_key_allowed(struct passwd *, BIGNUM *, Key **);
 int	 auth_rhosts_rsa_key_allowed(struct passwd *, char *, char *, Key *);
 int	 hostbased_key_allowed(struct passwd *, const char *, char *, Key *);
 int	 user_key_allowed(struct passwd *, Key *, int);
+char	*pubkey_format(const Key *key);
 void	 pubkey_auth_info(Authctxt *, const Key *, const char *, ...)
 	    __attribute__((__format__ (printf, 3, 4)));
 void	 auth2_record_userkey(Authctxt *, struct sshkey *);
diff --git a/auth2-pubkey.c b/auth2-pubkey.c
index b1b5b74..45a861a 100644
--- a/auth2-pubkey.c
+++ b/auth2-pubkey.c
@@ -214,40 +214,50 @@ done:
 	return authenticated;
 }
 
-void
-pubkey_auth_info(Authctxt *authctxt, const Key *key, const char *fmt, ...)
+char *
+pubkey_format(const Key *key)
 {
-	char *fp, *extra;
-	va_list ap;
-	int i;
-
-	extra = NULL;
-	if (fmt != NULL) {
-		va_start(ap, fmt);
-		i = vasprintf(&extra, fmt, ap);
-		va_end(ap);
-		if (i < 0 || extra == NULL)
-			fatal("%s: vasprintf failed", __func__);	
-	}
+	char *fp, *result;
 
 	if (key_is_cert(key)) {
 		fp = sshkey_fingerprint(key->cert->signature_key,
 		    options.fingerprint_hash, SSH_FP_DEFAULT);
-		auth_info(authctxt, "%s ID %s (serial %llu) CA %s %s%s%s", 
+		xasprintf(&result, "%s ID %s (serial %llu) CA %s %s",
 		    key_type(key), key->cert->key_id,
 		    (unsigned long long)key->cert->serial,
 		    key_type(key->cert->signature_key),
-		    fp == NULL ? "(null)" : fp,
-		    extra == NULL ? "" : ", ", extra == NULL ? "" : extra);
+		    fp == NULL ? "(null)" : fp);
 		free(fp);
 	} else {
 		fp = sshkey_fingerprint(key, options.fingerprint_hash,
 		    SSH_FP_DEFAULT);
-		auth_info(authctxt, "%s %s%s%s", key_type(key),
-		    fp == NULL ? "(null)" : fp,
-		    extra == NULL ? "" : ", ", extra == NULL ? "" : extra);
+		xasprintf(&result, "%s %s", key_type(key),
+		    fp == NULL ? "(null)" : fp);
 		free(fp);
 	}
+
+	return result;
+}
+
+void
+pubkey_auth_info(Authctxt *authctxt, const Key *key, const char *fmt, ...)
+{
+	char *extra, *pubkey;
+	va_list ap;
+	int i;
+
+	extra = NULL;
+	if (fmt != NULL) {
+		va_start(ap, fmt);
+		i = vasprintf(&extra, fmt, ap);
+		va_end(ap);
+		if (i < 0 || extra == NULL)
+			fatal("%s: vasprintf failed", __func__);
+	}
+
+	pubkey = pubkey_format(key);
+	auth_info(authctxt, "%s%s%s", pubkey, extra == NULL ? "" : ", ",
+	    extra == NULL ? "" : extra);
 	free(extra);
 }
 
-- 
2.4.10

>From 27d52173c07ad8aacddd919a6c4f4f382800a518 Mon Sep 17 00:00:00 2001
From: Vincent Brillault <git@xxxxxxxxx>
Date: Wed, 18 Nov 2015 21:32:06 +0100
Subject: [PATCH 5/8] auth2-pubkey: fill last_details on success

---
 auth2-pubkey.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/auth2-pubkey.c b/auth2-pubkey.c
index 45a861a..170cd24 100644
--- a/auth2-pubkey.c
+++ b/auth2-pubkey.c
@@ -79,7 +79,7 @@ userauth_pubkey(Authctxt *authctxt)
 {
 	Buffer b;
 	Key *key = NULL;
-	char *pkalg, *userstyle;
+	char *pkalg, *userstyle, *pubkey;
 	u_char *pkblob, *sig;
 	u_int alen, blen, slen;
 	int have_sig, pktype;
@@ -168,7 +168,8 @@ userauth_pubkey(Authctxt *authctxt)
 #ifdef DEBUG_PK
 		buffer_dump(&b);
 #endif
-		pubkey_auth_info(authctxt, key, NULL);
+		pubkey = pubkey_format(key);
+		auth_info(authctxt, "%s", pubkey);
 
 		/* test for correct signature */
 		authenticated = 0;
@@ -176,9 +177,12 @@ userauth_pubkey(Authctxt *authctxt)
 		    PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b),
 		    buffer_len(&b))) == 1) {
 			authenticated = 1;
+			authctxt->last_details = pubkey;
 			/* Record the successful key to prevent reuse */
 			auth2_record_userkey(authctxt, key);
 			key = NULL; /* Don't free below */
+		} else {
+			free(pubkey);
 		}
 		buffer_free(&b);
 		free(sig);
-- 
2.4.10

>From b3d808b192bbfad8f19942d1124d574a7d14d694 Mon Sep 17 00:00:00 2001
From: Vincent Brillault <git@xxxxxxxxx>
Date: Wed, 18 Nov 2015 21:36:55 +0100
Subject: [PATCH 6/8] auth2-hostbased: fill last_details on success

---
 auth2-hostbased.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/auth2-hostbased.c b/auth2-hostbased.c
index e2327cf..856efc9 100644
--- a/auth2-hostbased.c
+++ b/auth2-hostbased.c
@@ -60,7 +60,7 @@ userauth_hostbased(Authctxt *authctxt)
 {
 	Buffer b;
 	Key *key = NULL;
-	char *pkalg, *cuser, *chost, *service;
+	char *pkalg, *cuser, *chost, *service, *pubkey;
 	u_char *pkblob, *sig;
 	u_int alen, blen, slen;
 	int pktype;
@@ -132,15 +132,21 @@ userauth_hostbased(Authctxt *authctxt)
 	buffer_dump(&b);
 #endif
 
-	pubkey_auth_info(authctxt, key,
-	    "client user \"%.100s\", client host \"%.100s\"", cuser, chost);
+	pubkey = pubkey_format(key);
+	auth_info(authctxt,
+	    "%s, client user \"%.100s\", client host \"%.100s\"",
+	    pubkey, cuser, chost);
 
 	/* test for allowed key and correct signature */
 	authenticated = 0;
 	if (PRIVSEP(hostbased_key_allowed(authctxt->pw, cuser, chost, key)) &&
 	    PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b),
-			buffer_len(&b))) == 1)
+			buffer_len(&b))) == 1) {
 		authenticated = 1;
+		authctxt->last_details = pubkey;
+	} else {
+		free(pubkey);
+	}
 
 	buffer_free(&b);
 done:
-- 
2.4.10

>From 11a367de8fd87314fd6516306620136889e03c20 Mon Sep 17 00:00:00 2001
From: Vincent Brillault <git@xxxxxxxxx>
Date: Wed, 18 Nov 2015 23:53:15 +0100
Subject: [PATCH 7/8] privsep: Expose success auth methods

Unfortunately, in the monitor thread, not the same amount of data is
available when an key-base authentication succeed. It could be possible
to extract the key information of all key that pass through
mm_answer_keyverify, but linking it to the authentication success would
be dangerous.

Simply exposing the successul methods would already be a progress
---
 monitor.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/monitor.c b/monitor.c
index 395a6f6..c6b2236 100644
--- a/monitor.c
+++ b/monitor.c
@@ -335,6 +335,7 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
 {
 	struct mon_table *ent;
 	int authenticated = 0, partial = 0;
+	char *prev_auth_details;
 
 	debug3("preauth child monitor started");
 
@@ -366,6 +367,14 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
 		auth_submethod = NULL;
 		authenticated = (monitor_read(pmonitor, mon_dispatch, &ent) == 1);
 
+		if (authenticated) {
+			prev_auth_details = authctxt->auth_details;
+			xasprintf(&authctxt->auth_details, "%s%s%s",
+			    prev_auth_details ? prev_auth_details : "",
+			    prev_auth_details ? ", " : "", auth_method);
+			free(prev_auth_details);
+		}
+
 		/* Special handling for multiple required authentications */
 		if (options.num_auth_methods != 0) {
 			if (!compat20)
-- 
2.4.10

>From 2da3ca1fd0d48db8e6c7ebda0bf9002da4cbd45b Mon Sep 17 00:00:00 2001
From: Vincent Brillault <git@xxxxxxxxx>
Date: Thu, 19 Nov 2015 21:31:29 +0100
Subject: [PATCH 8/8] Mention SSH_USER_AUTH in the man page

---
 ssh.1 | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/ssh.1 b/ssh.1
index 5b35b6c..1af89b3 100644
--- a/ssh.1
+++ b/ssh.1
@@ -1402,6 +1402,10 @@ server IP address, and server port number.
 This variable contains the original command line if a forced command
 is executed.
 It can be used to extract the original arguments.
+.It Ev SSH_USER_AUTH
+This variable contains, for SSH2 only, a comma-separated list of authentication
+methods that were successfuly used to authenticate. When possible, these
+methods are extended with detailed information on the credential used.
 .It Ev SSH_TTY
 This is set to the name of the tty (path to the device) associated
 with the current shell or command.
-- 
2.4.10

_______________________________________________
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