SA Bugzilla – Bug 4550
RFE: Add secure user authentication to spamc/spamd protocol
Last modified: 2022-03-06 16:25:07 UTC
Bug 4546 brought up the issue that spamc -L in the absence of authentication of the user name opens vulnerabilities. In anticipation of that bug being closed by making spamd learning disabled by default, to be enabled only in a suitably secure environment, I'm opening this RFE to discuss how to do spamc -L securely in less restrictive environments. My first proposed solution is described in bug 4546 comment 7 but I would be happy to see ideas that are easier to implement and more lightweight to run.
Subject: Re: [RFE] Add secure user authentication to spamc/spamd protocol a) it should be a plugin, adding appropriate hooks to spamd to do the authentication b) I'll veto any patch that does not include performance numbers. Michael
What's involved in making something a plugin for spamd, as opposed to a SpamAssassin plugin? Do we have any spamd plugins already? If we go with something very lightweight, such as adding a password configuration option in user_prefs and spamc.conf and adding the password field to the protocol, then I don't see how or why it would be done as a plugin. I agree that if we do anything more, such as have a cryptographic secure handshaking authentication protocol, that would be best in a plugin.
Subject: Re: [RFE] Add secure user authentication to spamc/spamd protocol >------- Additional Comments From sidney@sidney.com 2005-08-21 18:01 ------- >What's involved in making something a plugin for spamd, as opposed to a >SpamAssassin plugin? Do we have any spamd plugins already? > > There are a couple of spamd plugin hooks now, no published plugins that make use of the hooks, but they are being used. Nothing extra involved to put the hook in spamd. >If we go with something very lightweight, such as adding a password >configuration option in user_prefs and spamc.conf and adding the password field >to the protocol, then I don't see how or why it would be done as a plugin. I >agree that if we do anything more, such as have a cryptographic secure >handshaking authentication protocol, that would be best in a plugin. > > It should be very easy for admins to create a plugin that will obtain the authentication information from their own, possibly custom, database. Then you just create the hooks and a basic plugin (this plugin can do perform the basic user_prefs config option password deal) and leave it up to the administrator to supply anything more complicated. That is the entire reason for plugins. FYI, there is actually a plugin hook that might be useful here, services_authorized_for_username. See the call in one of the BayesSQL modules for an example of how it is used. And, now that I think about it, given some spamd protocol header parsing foo and this plugin hook it would be trivial to add the basic, send a password in the header and check it against a value in a user_prefs file, solution. Michael
Subject: Re: [RFE] Add secure user authentication to spamc/spamd protocol Though the tough part, as always is fixing spamc to pass whatever it needs to pass. I'm thinking if we starting going down the spamd plugin road too far we're going to need to start having some sort of a "capabilities" command so that spamd/spamc can see what "extensions" (i.e. plugins) are supported.
Ok, some more details on a possible solution. 1) In spamd.raw parse_headers you add a spamd_parse_headers plugin hook. Initially I was thinking as just an else case but I can see value in letting plugins step in for any of the headers. 2) Then you add the services_authorized_for_username plugin hook in sub check and sub dotell passing in the appropriate service (ie check, report, process, learn, etc). 3) Then you add a new command line option to spamc that accepts a password and adds a password (or something else) header to the spamd protocol stream. 4) Then you create a plugin that has a hook that reads in the new password header and resonds to the services_authorized_for_username call with the appropriate response. It would also allow a new config option, choose whatever name you want really, that allows the user to specify the password. The plugin would then compare the passed in password with the one in the config and use that to generate it's response. Bonus points for doing interesting things with the password, like MD5 hashes or other obfuscation techniques. I know it's basic, but in the long run I see plugins that instead of looking in the user config it will do some database or ldap lookups to determine if the user is authorized.
The spamc password should be in the spamc config file, not on the command line (although a command line option that says to require a password is ok) so that the password does not show up in ps and command history lists. We should make it unlikely that someone will unthinkingly use password authentication where it can be sniffed. Perhaps we can enable it only when using a command line option --vpn that does nothing but indicate that the sysadmin claims that spamc/spamd communitcation is being done over a secure network, e.g., SSL or a VPN or on localhost. It would be a huge change to the protocol to make it session oriented. Without sessions and handshaking, the only way I see to avoid sending the password in the clear involves hashing the entire message... Oh, wait, don't spamc and spamd already read the entire message into memory? That means that they can hash it, which means that spamc can include a hash combining the message, the protocol command, the user name, the time, and the password. Spamd would use its copy of the password to verify the hash, which verifies the password as well as the integrity of the message transmission, with the password never being sent in the clear. I just has another idea... The password could be used by spamc to encrypt the message (actually password used to encrypt a session key used to encrypt the message). Spamd verifies the password by successfully decrypting, and sends back the result encrypted with the same session key. Not only do we get authentication without handshaking, but we get message confidentiality without the overhead of SSL. If we do that, it definitely has to be done as a plugin.
(In reply to comment #4) > I'm thinking if we starting going down the spamd plugin road too far > we're going to need to start having some sort of a "capabilities" > command so that spamd/spamc can see what "extensions" (i.e. plugins) > are supported. See attachment 3054 [details] of bug 4517.
Query for capabilities may not be all that important because spamc and spamd configurations have to match anyway. You would not use spamc as the client to an arbitrary spamd server. For example, if I set up a spamd server that uses the foo plugin for authentication, all my users have to have spamc configured to use the foo plugin. One step in the instructions to them on setting up the use of SpamAssassin is that they enable foo plugin in spamc. There will be no reason for spamc to query spamd for foo capabilities and no reasonable fallback behavior if spamd reports not having foo.
First off, using plugins to extend spamd is definitely worth doing. I've been planning to do it for a long time. I'm planning to move out all the odd little authentication systems (vpopmail, etc.) into plugins, instead of having that code littering around spamd.raw -- which is messy and buggy. Secondly, re capabilities. When thinking about spamc and spamd, the best parallel is to think in terms of the HTTP protocol, not SMTP or POP. so capabilities, and capability advertising, is not likely to be useful, in my opinion -- there's no "banner" sent from server to client, before the client issues its request, so how's the client going to know what the server supports? Instead of thinking in these terms, look at how the HTTP/HTTPS protocol solves these problems. As a result, in my opinion we should have: - no authentication on the userid passed (as in current trunk), if spamd has the command line flag to allow that. - simple password auth, modeled on HTTP basic authentication. Warn, as with HTTP basic auth, that the pwd is sniffable. - simple password auth as above, over an SSL channel. Some notes: - The last one is a secure way to do it. Note that there is *no* additional secure method -- because it's easier just to use SSL and let *that* do the hard work of keeping the pwd secret, instead of inventing our own secure protocol! Believe me, I've been down that road before. - If connection open/close handshaking is an issue, then we add a connection-persistence hook to the protocol, similar to HTTP Keepalive in HTTP/1.0 vs HTTP/1.1. we could also do the "session key" idea, but I think keepalive is more useful. - If we have >1 methods of userid auth for learning, we will need some way for spamd sysadmins to indicate their site policy (e.g. "all users must use auth over SSL"). PS: Michael, +1 in general on the comment 5 idea. >add a spamd_parse_headers plugin hook. Initially I was thinking as just an >else case but I can see value in letting plugins step in for any of the >headers. +1 for either; I can see the usefulness of the latter, as long as the plugin doesn't interfere with other plugins (or the core) seeing the header. PPS: spamc password UI -- why not emulate rsync? support passing it in through an environment var, it works well for rsync, should work here too. PPPS: I don't see how att 3054 on bug 4517 is relevant -- the have_plugin() call? not getting it.
I mostly agree with Justin's comment 9, with these further comments: No auth is fine. --auth-ident is useful with it. Do we want to implement it as a plugin, though? In HTTP basic authentication, the client sends a name/password in the clear in two fields in the protocol. The server can send a request for authentication to the client and the client then has to respond with it. In our case there is no need for spamd to send a request for authentication to spamc. HTTP encodes the password in BASE64, which seems like a good idea for our protocol. So what we are left with that we add another field to the record that spamc sends in addition to the existing user name field, which will contain 'pwd:' and the BASE64 encoded password. That basic authentication becomes secure when used with SSL or with a VPN or SSH tunnel. I agree that it would be a headache to design our own secure protocol. However, we should make sure that the plugin API makes it possible for someone to use a plugin that encrypts/decrypts the message using a per-user key that spamc and spamd have access to. That provides security that is a lot more lightweight than establishing an SSL session each time or requiring every user to have a VPN tunnel to the server. I might write such a plugin. I don't see any reason for persistent connections. Let's not add a keepalive header unless a reason for it comes up. If one does, I agree with the HTTP 1.0 style of header that has no keepalive by default and a 'connection: keepalive' field to keep the connection open.
Created attachment 3098 [details] Proof of Concept Patch Here is a proof of concept patch to illustrate my idea. It's far from complete but I think you can get the general idea. We add a plugin hook for spamd_parse_headers to pick up any un-handled headers (ie our Password: foo header that we want to add) and we insert the services_authorized_for_username hook at the top of dotell, and eventually maybe check if we wanted and then act accordingly for the return value. The plugin checks the password against an htpasswd file that we setup on the server. Like I said, strictly a proof of concept that I whipped up in about an hour, it needs some work but shows the diirection I think we should head.
There's a relatively new feature that has been added to the TLS standard by the IETF and that is implemented in OpenSSL called TLS-PSK. I want to mention it here so the idea doesn't get lost. It may be a good way to do this. Here is a brief comment about it quoted from a cryptography expert who was talking about it on a mailing list I subscribe to. Google to get full context from the mailing list archive if you are curious. Peter Gutmann wrote: > TLS-PSK fixes this problem by providing mutual authentication of client and > server as part of the key exchange. Both sides demonstrate proof-of- > possession of the password (without actually communicating the password), if > either side fails to do this then the TLS handshake fails. If we use TLS-PSK for the SSL spamc/spamd connection, we get the secure authentication all taken care of with a standards-based protocol. I do need to read more about it to find out if it is indeed practical to use it with a separate password for each user in many-users environment. I don't yet know how OpenSSL deals with storing the various client passwords on the server.
Title: Secure user authentication in the spamd protocol This was a suggested idea for the Google Summer of Code 2006; I'm adding it to the bugzilla for future use, and in case anyone feels like implementing it. Subject ID: spamassassin-secure-user-auth Keywords: spamd, protocol, tls, perl Description: http://issues.apache.org/SpamAssassin/show_bug.cgi?id=4550 : a secure method to authenticate users over a spamc/spamd connection. Possible Mentors: Justin Mason (jm at jmason.org)
I don't think this is likely to make 3.2.0
> I don't think this is likely to make 3.2.0 Apparently it's not going to happen for 3.3.0, changing target to 'future'.
Closing ancient stale bug.