Index | Thread | Search

From:
gilles@poolp.org
Subject:
smtpd: table auth offloading
To:
tech@openbsd.org
Date:
Thu, 23 May 2024 21:55:50 +0000

Download raw body.

Thread
Hello,

Just a mail to propose an idea that I discussed with op@ in private.

In smtpd, there are two codepaths for authentication:

1- if no table was provided, smtpd relies on bsd_auth(3)

2- if a table is provided, it performs a K_CREDENTIALS lookup which,
   given a username as a key, looks up the crypt(3)-ed password that
   is then compared to the one that the user provided.

When you provide a table to the auth keyword, ie:

   listen on all [...] auth <foobar>

Regardless of the backend used by <foobar> you take the second path,
which is great because any backend that supports key-value lookup is
usable as an authentication backend in smtpd: inline, file, db, ...

Problem is that some backends do not expect to be queried this way.

For example, OpenLDAP and OpenBSD's ldapd have an existing attribute
userPassword which is mandatory but which we can't use because it is
not in the format we expect and not meant to be fed to crypt(3) like
we do for all other table backends. In such cases, we'd rather let a
table backend do the validation and tell us if it succeeded.

This diff introduces a new lookup service K_AUTH which is only meant
to be used by table backends that need custom auth validation. If we
authenticate against a table that supports K_AUTH, then instead of a
K_CREDENTIALS lookup we forward the username:password so the backend
can tell us if authentication succeeded or not.

ok ?


Index: lka.c
===================================================================
RCS file: /cvs/src/usr.sbin/smtpd/lka.c,v
diff -u -p -r1.248 lka.c
--- lka.c	20 Jan 2024 09:01:03 -0000	1.248
+++ lka.c	23 May 2024 21:07:15 -0000
@@ -720,6 +720,7 @@ static int
 lka_authenticate(const char *tablename, const char *user, const char *password)
 {
 	struct table		*table;
+	char	       		 offloadkey[LINE_MAX];
 	union lookup		 lk;
 
 	log_debug("debug: lka: authenticating for %s:%s", tablename, user);
@@ -728,6 +729,25 @@ lka_authenticate(const char *tablename, 
 		log_warnx("warn: could not find table %s needed for authentication",
 		    tablename);
 		return (LKA_TEMPFAIL);
+	}
+
+	/* table backend supports authentication offloading */
+	if (table_check_service(table, K_AUTH)) {
+		if (!bsnprintf(offloadkey, sizeof(offloadkey), "%s:%s", user, password)) {
+			log_warnx("warn: offload key serialization failed for %s:%s",
+			    tablename, user);
+			return (LKA_TEMPFAIL);
+		}
+		switch (table_match(table, K_AUTH, offloadkey)) {
+		case -1:
+			log_warnx("warn: user credentials lookup fail for %s:%s",
+			    tablename, user);
+			return (LKA_TEMPFAIL);
+		case 0:
+			return (LKA_PERMFAIL);
+		default:
+			return (LKA_OK);
+		}
 	}
 
 	switch (table_lookup(table, K_CREDENTIALS, user, &lk)) {
Index: smtpd-api.h
===================================================================
RCS file: /cvs/src/usr.sbin/smtpd/smtpd-api.h,v
diff -u -p -r1.36 smtpd-api.h
--- smtpd-api.h	23 Dec 2018 16:06:24 -0000	1.36
+++ smtpd-api.h	23 May 2024 21:07:15 -0000
@@ -135,8 +135,9 @@ enum table_service {
 	K_RELAYHOST	= 0x200,	/* returns struct relayhost	*/
 	K_STRING	= 0x400,
 	K_REGEX		= 0x800,
+	K_AUTH		= 0x1000,
 };
-#define K_ANY		  0xfff
+#define K_ANY		  0xffff
 
 enum {
 	PROC_TABLE_OK,
Index: table.5
===================================================================
RCS file: /cvs/src/usr.sbin/smtpd/table.5,v
diff -u -p -r1.14 table.5
--- table.5	2 May 2024 18:14:33 -0000	1.14
+++ table.5	23 May 2024 21:07:15 -0000
@@ -113,6 +113,19 @@ Here, the user is allowed to send mail f
 .Bd -unfilled -offset indent
 .Ic listen on Ar interface Cm auth Oo Ar ... Oc Cm senders Pf < Ar table Ns >
 .Ed
+.Ss Authentication tables
+In most cases,
+authentication is best served by a credentials table which provide information that
+.Nm smtpd 8
+can use to validate itself using
+.Nm bsd_auth 3
+or
+.Nm crypt 3 .
+.Pp
+In some cases,
+this is not desireable and the validation needs to be offloaded.
+The authentication tables support doing a check on a username and password,
+returning success or failure based on custom logic.
 .Ss Domain tables
 Domain tables are simple lists of domains or hosts.
 .Bd -unfilled -offset indent
Index: table.c
===================================================================
RCS file: /cvs/src/usr.sbin/smtpd/table.c,v
diff -u -p -r1.52 table.c
--- table.c	7 May 2024 12:10:06 -0000	1.52
+++ table.c	23 May 2024 21:07:15 -0000
@@ -83,6 +83,7 @@ table_service_name(enum table_service s)
 	case K_RELAYHOST:	return "relayhost";
 	case K_STRING:		return "string";
 	case K_REGEX:		return "regex";
+	case K_AUTH:		return "auth";
 	}
 	return "???";
 }
@@ -116,6 +117,8 @@ table_service_from_name(const char *serv
 		return K_STRING;
 	if (!strcmp(service, "regex"))
 		return K_REGEX;
+	if (!strcmp(service, "auth"))
+		return K_AUTH;
 	return (-1);
 }