Basic Authentication has...issues...

Frank Earl
24 January 2013, 23:35

Hiawatha version: 8.7
Operating System: Poky 8.0 (ARMv5 (TI L138) target)

Basic Authentication appears to only be set to understand the basic crypt() functionality looking at the code, but at least glibc/eglibc's crypt() understands an extended salt format in the hashed password structure. The salt is of the format $id$salt$hash where id is a keyval corresponding to 1, 5, or 6 (MD5, SHA-256, or SHA-512 respectively), the salt is a 16 character string and the hash is an extended length field. In the application I'm re-implementing Hiawatha into, we have an authentication database using SHA-512 hashing to allow for passphrases and the like to be something in the ballpark of what is required by FIPS.

The other problem is that there's some issues with the HTTP authentication process in that the Bas64 decode ends up with corruption unless you ammend PolarSSL's decode routine to put a null on the end of the decoded string properly.

I think I've got my issue fixed, but I thought I'd start the discussion upstream so you're looking at the issues when you get to them. I'll be happy to provide patchsets for both issues...
Hugo Leisink
25 January 2013, 08:27
I've added a NULL byte at the end of the BASE64 decoded password string.

Please, show me your patch for the crypt() issue. I'm not sure what you mean with that.
Frank Earl
6 February 2013, 18:22
The problem lies in that there's quite a bit more hash capabilities than you're supporting there (look at the manpage for it on a linux box...)- with two characters for the salt, you're supporting the original mode.

Frank Earl
6 February 2013, 18:26
Glibc Notes
The glibc2 version of this function supports additional encryption

If salt is a character string starting with the characters "$id$" fol‐
lowed by a string terminated by "$":


then instead of using the DES machine, id identifies the encryption
method used and this then determines how the rest of the password
string is interpreted. The following values of id are supported:

ID | Method
1 | MD5
2a | Blowfish (not in mainline glibc; added in some
| Linux distributions)
5 | SHA-256 (since glibc 2.7)
6 | SHA-512 (since glibc 2.7)

So $5$salt$encrypted is an SHA-256 encoded password and
$6$salt$encrypted is an SHA-512 encoded one.

"salt" stands for the up to 16 characters following "$id$" in the salt.
The encrypted part of the password string is the actual computed pass‐
word. The size of this string is fixed:

MD5 | 22 characters
SHA-256 | 43 characters
SHA-512 | 86 characters

The characters in "salt" and "encrypted" are drawn from the set
[a–zA–Z0–9./]. In the MD5 and SHA implementations the entire key is
significant (instead of only the first 8 bytes in DES).

The patch I did doesn't presume that you're using these modes, but the application I'm doing needs to have support there since we're a heightened security design that needs to use the 512-bit mode for passphrases.

diff -rupN hiawatha-8.7-orig/src/httpauth.c hiawatha-8.7/src/httpauth.c
--- hiawatha-8.7-orig/src/httpauth.c 2013-01-09 12:18:21.000000000 -0700
+++ hiawatha-8.7/src/httpauth.c 2013-01-24 13:03:11.305384682 -0700
@@ -205,8 +205,8 @@ static char *get_A1(t_session *session,
static int basic_http_authentication(t_session *session, char *auth_str) {
size_t auth_len;
- int retval;
- char *auth_user, *auth_passwd, *passwd, *encrypted, salt[3];
+ int retval, loop;
+ char *auth_user, *auth_passwd, *passwd, *encrypted, salt[21];

auth_len = strlen(auth_str);
if ((auth_user = (char*)malloc(auth_len + 1)) == NULL) {
@@ -249,9 +249,43 @@ static int basic_http_authentication(t_s
return ha_DENIED;

+#if 0
+ /*
+ * This presumes that the salt will ALWAYS be two bytes, which is...
+ * not always so. GNU's crypt() call is expanded and the code that's
+ * in the #else side of this #if 0 block handles detection of whether
+ * or not we're working with an extended salt or not in the password
+ * file. This is being done to account for FreeWave's using SHA-512
+ * bit hashes to support something more akin to a FIPS compliant
+ * password in the file.
+ *
+ * FCE (01-24-13)
+ */
salt[0] = *passwd;
salt[1] = *(passwd + 1);
salt[2] = '\0';
+ memcpy(salt, passwd, 3);
+ if ((salt[0] == '$') && (salt[2] == '$') && ((salt[1] == '1') || (salt[1] == '5') || (salt[1] == '6')))
+ {
+ /* GNU extended crypt() salt - nab the rest up to the closing '$' */
+ for(loop = 0; loop < 17; loop++)
+ {
+ salt[3 + loop] = passwd[3 + loop];
+ if (passwd[3 + loop] == '$')
+ {
+ loop++;
+ break;
+ }
+ }
+ salt[3 + loop] = 0;
+ }
+ else
+ {
+ /* Regular crypt() salt */
+ salt[2] = 0;
+ }
encrypted = crypt(auth_passwd, salt);

/* Password match?

It's probably not QUITE the way you want it done in your code- which is why I started the conversation. It still needs to account for the widened support because most servers handle this one correctly- and I really, really want to use yours since it is one of the lightest, yet most secure ones out there.
Hugo Leisink
6 February 2013, 19:24
From the manual page:
The encrypted part of the password string is the actual computed password.

I don't know who write this manual page, but this is crap. What encrypted part? The idea is that the crypt() function encrypts a password. So, I don't have a 'encrypted part of the password'. This manual page is totally useless.
Frank Earl
7 February 2013, 00:00
Sigh... Shame you opted for the snippy approach. Well, I posted this in an attempt to be a good dev and a contributor to the whole community. I still am- so rather than blowing you off, I'll try this again...

The salt either comprises the traditional one or one delimited by "$id$salt$"

Here's an example string from out of my passwd/auth file: "$6$fW$SFvZG8RWNwlMz.2MOGnJxhiVKAEtGC60JXA2V3woO9QilCjtTsnbBHEGpogUYZp9/qd87mxNa5aiqSRhYEn8I0"

In that example it's an SHA2 512-bit hashed string wherein:

- "$6" tells crypt that we're doing a "SHA-512" hash pass to one-way encrypt the password I've encrypted there.
- "fW$" is the salt and it's delimiter.
- The rest being the "encrypted part of the password" that you're hashing. - This is an EXPANDED explanation of the function you're using and explains in even more detail what you're claiming is "totally useless"...

With the original code, password lengths are limited due to your use the 56-bit DES hashing implementation. Unfortunately, that use of the stock algorithm tends to, depending on target platform, effectively limit passwords to 8 chars and truncates past that. Even with the ban functionality, it doesn't meet FIPS level security requirements because of this. MD5 limits to about 72 and the SHA-2 has a slightly larger limit. You can't access the MD5, optional Blowfish, or SHA2 hashing functionalities with the traditional DES operation mode of crypt(3)- you have to use the "modular" mode operation that's present on pretty much any modern POSIX compliant OS. The patches I provided offer this functionality to be selectable by the user at their discretion.

Now, while Basic's "pretty insecure" as an authentication mode- it is only really so if you're talking to the server in plaintext mode. If you're doing HTTPS, it's about as secure as Digest is because it's all wrapped in an SSL conversation. 8-chars isn't really all that good- and in the application we're discussing, they're going to have the link largely locked down in an HTTPS session with the interface and have NO HTTP one possible. There's more to this than that, but basically, presume that you have to use the same sort of authentication that a console login would use in this situation- and the DES mode's just not usable at-all.

The patch I provided handles either original or modular mode gracefully and does both with a system that works in this manner.
Hugo Leisink
7 February 2013, 00:28
I understand now what needs to be done, thanks. This has nothing to do with being snippy. It's about getting a bit frustrated about a crappy manual page. Now I understand how it works, I see what's wrong in the manual page. Anyway, I'll make it availble for the next release. Should be simple to do.
This topic has been closed.