[Dovecot] [PATCH] SCRAM-SHA-1 authentication

2011-09-15 Thread Florian Zeitz
Hy,

over the last days I have implemented SCRAM-SHA-1 in Dovecot's 2.1
branch. It does not do SCRAM-SHA-1-PLUS, but should be extendable enough
to introduce it later.

There are some checks for the message format which (assuming the client
acts correclty) are not strictly necessary during parsing. This is
partially in the hope that it might aid client implementers, partially
because it (IMHO) improves readability when checking against the RFC.

Also errors found in this way could be sent to the client, this is
however strictly OPTIONAL in the RFC, for now they are just logged.

Some of the variable names are rather long. This is in order to have
them match the terms introduced in the RFC, again I expect it to help
readability (maybe my recent Objective-C programming showing though).

I do feel somewhat insecure about my usage of some lib functions.
Hopefully no API has been abused too much.

I also note that there are a lot of fields in the scram_auth_request
struct. I think they are all there for a reason, however feel free to
prove me wrong.

Attached is a hg export.
It also includes a hmac-sha1 implementation, an adaption off of the
hmac-md5 implementation already in Dovecot. I guess those should
eventually be merged into a hash-independent hmac implementation, but I
figured this would have to do for now.

The implementation has been tested against GNU SASL and does appear to
work fine. (The command line was `gsasl -m SCRAM-SHA-1 -a user -p pass
--imap host` for those curious)

Regards
Florian "Florob" Zeitz
# HG changeset patch
# User Florian Zeitz 
# Date 1316132569 -7200
# Node ID 9d3cb2ef74d8a235ff86b4f8d644a28a47c17a70
# Parent  61d3544f8fdf03558055d240f5815dc1eac7fb66
lib: Add hmac-sha1 adapted from hmac-md5

diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -43,6 +43,7 @@
hex-binary.c \
hex-dec.c \
hmac-md5.c \
+   hmac-sha1.c \
home-expand.c \
hostpid.c \
imem.c \
diff --git a/src/lib/hmac-sha1.c b/src/lib/hmac-sha1.c
new file mode 100644
--- /dev/null
+++ b/src/lib/hmac-sha1.c
@@ -0,0 +1,52 @@
+/*
+ * HMAC-SHA1 (RFC-2104) implementation.
+ *
+ * Copyright (c) 2004 Andrey Panin 
+ * Copyright (c) 2011 Florian Zeitz 
+ *
+ * This software is released under the MIT license.
+ */
+
+#include "lib.h"
+#include "hmac-sha1.h"
+#include "safe-memset.h"
+
+void hmac_sha1_init(struct hmac_sha1_context *ctx,
+  const unsigned char *key, size_t key_len)
+{
+   int i;
+   unsigned char sha1key[20];
+   unsigned char k_ipad[64];
+   unsigned char k_opad[64];
+
+   if (key_len > 64) {
+   sha1_get_digest(key, key_len, sha1key);
+   key = sha1key;
+   key_len = 20;
+   }
+
+   memcpy(k_ipad, key, key_len);
+   memset(k_ipad + key_len, 0, 64 - key_len);
+   memcpy(k_opad, k_ipad, 64);
+
+   for (i = 0; i < 64; i++) {
+   k_ipad[i] ^= 0x36;
+   k_opad[i] ^= 0x5c;
+   }
+
+   sha1_init(&ctx->ctx);
+   sha1_loop(&ctx->ctx, k_ipad, 64);  
+   sha1_init(&ctx->ctxo);
+   sha1_loop(&ctx->ctxo, k_opad, 64);   
+
+   safe_memset(k_ipad, 0, 64);
+   safe_memset(k_opad, 0, 64);
+}
+
+void hmac_sha1_final(struct hmac_sha1_context *ctx, unsigned char *digest)
+{
+   sha1_result(&ctx->ctx, digest);
+
+   sha1_loop(&ctx->ctxo, digest, 20);
+   sha1_result(&ctx->ctxo, digest);
+}
diff --git a/src/lib/hmac-sha1.h b/src/lib/hmac-sha1.h
new file mode 100644
--- /dev/null
+++ b/src/lib/hmac-sha1.h
@@ -0,0 +1,22 @@
+#ifndef HMAC_SHA1_H
+#define HMAC_SHA1_H
+
+#include "sha1.h"
+
+struct hmac_sha1_context {
+   struct sha1_ctxt ctx, ctxo;
+};
+
+void hmac_sha1_init(struct hmac_sha1_context *ctx,
+  const unsigned char *key, size_t key_len);
+void hmac_sha1_final(struct hmac_sha1_context *ctx,
+   unsigned char digest[SHA1_RESULTLEN]);
+
+
+static inline void
+hmac_sha1_update(struct hmac_sha1_context *ctx, const void *data, size_t size)
+{
+   sha1_loop(&ctx->ctx, data, size);
+}
+
+#endif
# HG changeset patch
# User Florian Zeitz 
# Date 1316132640 -7200
# Node ID e559c59fda1f93d2c1319f9f5d2d8b7d83cfc8ff
# Parent  9d3cb2ef74d8a235ff86b4f8d644a28a47c17a70
auth: Implement the SCRAM-SHA-1 SASL mechanism

diff --git a/src/auth/Makefile.am b/src/auth/Makefile.am
--- a/src/auth/Makefile.am
+++ b/src/auth/Makefile.am
@@ -81,6 +81,7 @@
mech-gssapi.c \
mech-ntlm.c \
mech-otp.c \
+   mech-scram-sha1.c \
mech-skey.c \
mech-rpa.c \
mech-apop.c \
diff --git a/src/auth/mech-scram-sha1.c b/src/auth/mech-scram-sha1.c
new file mode 100644
--- /dev/null
+++ b/src/auth/mech-scram-sha1.c
@@ -0,0 +1,397 @@
+/*
+ * SCRAM-SHA-1 SASL authentication, see RFC-5802
+ *
+ * Copyr

Re: [Dovecot] [PATCH] SCRAM-SHA-1 authentication

2011-09-17 Thread Florian Zeitz
Am 16.09.2011 14:40, schrieb Timo Sirainen:
> On Fri, 2011-09-16 at 02:47 +0200, Florian Zeitz wrote:
> 
> Looks pretty good. Below are a few things I noticed. I could fix these
> myself next week also, or you can do them during weekend if you want
> to. :)
> 
I decided to do it myself, hope this fixes all issues.

>  - Could be nicer if client->proof was stored base64-decoded, so its
> validity could be checked and also later there wouldn't be need to
> base64-encode signature when testing it.
> 
>  - Doesn't verify_credentials() need to check the credentials in any way
> that it contains expected (sized) data? Anything is allowed?
> 
I don't think it needs to. The password read from the database can
legitimately have any length and from the client it just takes a base64
encoded SHA-1 hash. The correct size of that was previously implicitly
checked when comparing the base64 encoded data (strings of different
length don't compare equal). It's now explicitly checked after base64
decoding the client proof.
# HG changeset patch
# User Florian Zeitz 
# Date 1316132569 -7200
# Node ID 9d3cb2ef74d8a235ff86b4f8d644a28a47c17a70
# Parent  61d3544f8fdf03558055d240f5815dc1eac7fb66
lib: Add hmac-sha1 adapted from hmac-md5

diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -43,6 +43,7 @@
hex-binary.c \
hex-dec.c \
hmac-md5.c \
+   hmac-sha1.c \
home-expand.c \
hostpid.c \
imem.c \
diff --git a/src/lib/hmac-sha1.c b/src/lib/hmac-sha1.c
new file mode 100644
--- /dev/null
+++ b/src/lib/hmac-sha1.c
@@ -0,0 +1,52 @@
+/*
+ * HMAC-SHA1 (RFC-2104) implementation.
+ *
+ * Copyright (c) 2004 Andrey Panin 
+ * Copyright (c) 2011 Florian Zeitz 
+ *
+ * This software is released under the MIT license.
+ */
+
+#include "lib.h"
+#include "hmac-sha1.h"
+#include "safe-memset.h"
+
+void hmac_sha1_init(struct hmac_sha1_context *ctx,
+  const unsigned char *key, size_t key_len)
+{
+   int i;
+   unsigned char sha1key[20];
+   unsigned char k_ipad[64];
+   unsigned char k_opad[64];
+
+   if (key_len > 64) {
+   sha1_get_digest(key, key_len, sha1key);
+   key = sha1key;
+   key_len = 20;
+   }
+
+   memcpy(k_ipad, key, key_len);
+   memset(k_ipad + key_len, 0, 64 - key_len);
+   memcpy(k_opad, k_ipad, 64);
+
+   for (i = 0; i < 64; i++) {
+   k_ipad[i] ^= 0x36;
+   k_opad[i] ^= 0x5c;
+   }
+
+   sha1_init(&ctx->ctx);
+   sha1_loop(&ctx->ctx, k_ipad, 64);
+   sha1_init(&ctx->ctxo);
+   sha1_loop(&ctx->ctxo, k_opad, 64);
+
+   safe_memset(k_ipad, 0, 64);
+   safe_memset(k_opad, 0, 64);
+}
+
+void hmac_sha1_final(struct hmac_sha1_context *ctx, unsigned char *digest)
+{
+   sha1_result(&ctx->ctx, digest);
+
+   sha1_loop(&ctx->ctxo, digest, 20);
+   sha1_result(&ctx->ctxo, digest);
+}
diff --git a/src/lib/hmac-sha1.h b/src/lib/hmac-sha1.h
new file mode 100644
--- /dev/null
+++ b/src/lib/hmac-sha1.h
@@ -0,0 +1,22 @@
+#ifndef HMAC_SHA1_H
+#define HMAC_SHA1_H
+
+#include "sha1.h"
+
+struct hmac_sha1_context {
+   struct sha1_ctxt ctx, ctxo;
+};
+
+void hmac_sha1_init(struct hmac_sha1_context *ctx,
+  const unsigned char *key, size_t key_len);
+void hmac_sha1_final(struct hmac_sha1_context *ctx,
+   unsigned char digest[SHA1_RESULTLEN]);
+
+
+static inline void
+hmac_sha1_update(struct hmac_sha1_context *ctx, const void *data, size_t size)
+{
+   sha1_loop(&ctx->ctx, data, size);
+}
+
+#endif
# HG changeset patch
# User Florian Zeitz 
# Date 1316132640 -7200
# Node ID fea09a51a98b789cba58a0ca4f0e220f2ae1f646
# Parent  9d3cb2ef74d8a235ff86b4f8d644a28a47c17a70
auth: Implement the SCRAM-SHA-1 SASL mechanism

diff --git a/src/auth/Makefile.am b/src/auth/Makefile.am
--- a/src/auth/Makefile.am
+++ b/src/auth/Makefile.am
@@ -81,6 +81,7 @@
mech-gssapi.c \
mech-ntlm.c \
mech-otp.c \
+   mech-scram-sha1.c \
mech-skey.c \
mech-rpa.c \
mech-apop.c \
diff --git a/src/auth/mech-scram-sha1.c b/src/auth/mech-scram-sha1.c
new file mode 100644
--- /dev/null
+++ b/src/auth/mech-scram-sha1.c
@@ -0,0 +1,405 @@
+/*
+ * SCRAM-SHA-1 SASL authentication, see RFC-5802
+ *
+ * Copyright (c) 2011 Florian Zeitz 
+ *
+ * This software is released under the MIT license.
+ */
+
+#include "auth-common.h"
+#include "base64.h"
+#include "buffer.h"
+#include "hmac-sha1.h"
+#include "randgen.h"
+#include "safe-memset.h"
+#include "str.h"
+#include "strfuncs.h"
+#include "mech.h"
+
+struct scram_auth_request {
+   struct auth_request auth_request;
+
+   pool_t pool;

Re: [Dovecot] Managesieve AUTHENTICATE with IDN using UTF-8 or encode into ACE?

2012-02-21 Thread Florian Zeitz
Am 21.02.2012 16:05, schrieb Christian Rohmann:
> Hey dovecot-users,
> 
> clients that speak telnet based protocols like http, smtp usually encode
> IDN (International Domain Names) containing UTF-8 characters into ACE
> (ASCII Compatible Encoding) punycode.
> 
> I am wondering what the case was with managesieve (RFC 5804). For
> example if I was using a username like "user@äüö.example.com" with the
> AUTHENTICATE command. Would that need to be encoded into ACE or be left
> in UTF-8? Reading section "2.1. AUTHENTICATE Command" or "4. Formal
> Syntax" suggests that quoted or literal UTF-8 is correct.
>
Hy,
what you're asking for is the correct format of the simple username.
This is in fact deployment specific. There is in general no requirement
to include the domain at all. The only requirement for to fulfil is that
it must not contain any codepoints prohibited by the SASLPrep stringprep
profile.
As such both the ACE and the IDN variant are valid. Which one to choose,
or whether to use only "user" depends on local policies.
There might be some implementation specifics in dovecot/pigeonhole that
I'm not aware of though.

Regards,
Florian


Re: [Dovecot] Using plaintext auth and SSL

2012-03-19 Thread Florian Zeitz
Am 20.03.2012 01:16, schrieb Eliezer Croitoru:
> On 20/03/2012 01:37, Jeff Simmons wrote:
>> On Monday, March 19, 2012 04:16:46 pm you wrote:
>>> On 3/19/2012 4:04 PM, Jeff Simmons wrote:
 I'm working with a company that presently has a Linux mailserver which
 all users have (no shell) accounts on. Mail is accessed via pop3 with
 plaintext authentication. They want to move to a system using imap with
 SSL. I'm building them a new server. I'd like to offer both for a while
 so we can work the bugs out and migrate users over to SSL imap over
 time. It appears that in order to limit the imap connections to SSL I
 will need to run two separate instances of Dovecot. Is this correct?
>>>
>>> I only have SSL or TLS connections enabled and I only have one copy of
>>> Dovecot running.
>>
>> Let me rephrase that. I want to run plaintext authentication pop3 and
>> ssl/tls
>> only authentication imap. The 'allow plaintext authentication'
>> configuration
>> directive appears to be global, meaning I will need to run two
>> instances of
>> dovecot for a while. Is that correct, or can this be done on a single
>> instance
>> of dovecot?
>>
> there is no connection between the plaintext auth to the ssl\tls layer.
> you can just change the in the service section of the 10-master.conf
> file of the imap to no imap at all and use only imaps listener with port
> for your choose such as 143 or 993 and you will have a only imap over ssl.
> 
Because it is going to drive me insane if I don't ask:
Is there really no way to archive this with a modern (aka. STARTTLS
based) IMAP setup?


[Dovecot] [PATCH] Generalize HMAC implementation

2012-09-04 Thread Florian Zeitz
Hello everyone and Timo in particular,

about a year ago I implemented a SHA-1 variant of the HMAC(-MD5) present
in Dovecot.
I had always disliked this a bit, because it replicates a lot of code.
This patch generalizes the HMAC function to take a hash_method struct as
parameter, and changes existing code which uses the "old" HMAC function
to use this new one.

I'm not really sure this is actually a good idea, but I still felt I
should provide the code in case you would want to merge it upstream.

Attached is the patch as a hg export based on the revision of
dovecot-2.2 current at the time of writing.

Regards,
Florian Zeitz
# HG changeset patch
# User Florian Zeitz 
# Date 1346280236 -7200
# Node ID e2f682fab829c2ef23a050f884191f57e2fb5d60
# Parent  9bc2e718392ceaa327f14b50163232b629cd54d1
lib: Generalize hmac to be hash independent

diff --git a/src/auth/mech-cram-md5.c b/src/auth/mech-cram-md5.c
--- a/src/auth/mech-cram-md5.c
+++ b/src/auth/mech-cram-md5.c
@@ -7,7 +7,9 @@
 #include "ioloop.h"
 #include "buffer.h"
 #include "hex-binary.h"
-#include "hmac-md5.h"
+#include "hmac-cram-md5.h"
+#include "hmac.h"
+#include "md5.h"
 #include "randgen.h"
 #include "mech.h"
 #include "passdb.h"
@@ -50,7 +52,7 @@
 {

unsigned char digest[MD5_RESULTLEN];
-struct hmac_md5_context ctx;
+struct hmac_context ctx;
const char *response_hex;
 
if (size != CRAM_MD5_CONTEXTLEN) {
@@ -59,9 +61,10 @@
return FALSE;
}
 
+   hmac_init(&ctx, NULL, 0, &hash_method_md5);
hmac_md5_set_cram_context(&ctx, credentials);
-   hmac_md5_update(&ctx, request->challenge, strlen(request->challenge));
-   hmac_md5_final(&ctx, digest);
+   hmac_update(&ctx, request->challenge, strlen(request->challenge));
+   hmac_final(&ctx, digest);
 
response_hex = binary_to_hex(digest, sizeof(digest));
 
diff --git a/src/auth/mech-scram-sha1.c b/src/auth/mech-scram-sha1.c
--- a/src/auth/mech-scram-sha1.c
+++ b/src/auth/mech-scram-sha1.c
@@ -9,7 +9,8 @@
 #include "auth-common.h"
 #include "base64.h"
 #include "buffer.h"
-#include "hmac-sha1.h"
+#include "hmac.h"
+#include "sha1.h"
 #include "randgen.h"
 #include "safe-memset.h"
 #include "str.h"
@@ -44,23 +45,23 @@
   const unsigned char *salt, size_t salt_size, unsigned int i,
   unsigned char result[SHA1_RESULTLEN])
 {
-   struct hmac_sha1_context ctx;
+   struct hmac_context ctx;
unsigned char U[SHA1_RESULTLEN];
unsigned int j, k;
 
/* Calculate U1 */
-   hmac_sha1_init(&ctx, str, str_size);
-   hmac_sha1_update(&ctx, salt, salt_size);
-   hmac_sha1_update(&ctx, "\0\0\0\1", 4);
-   hmac_sha1_final(&ctx, U);
+   hmac_init(&ctx, str, str_size, &hash_method_sha1);
+   hmac_update(&ctx, salt, salt_size);
+   hmac_update(&ctx, "\0\0\0\1", 4);
+   hmac_final(&ctx, U);
 
memcpy(result, U, SHA1_RESULTLEN);
 
/* Calculate U2 to Ui and Hi */
for (j = 2; j <= i; j++) {
-   hmac_sha1_init(&ctx, str, str_size);
-   hmac_sha1_update(&ctx, U, sizeof(U));
-   hmac_sha1_final(&ctx, U);
+   hmac_init(&ctx, str, str_size, &hash_method_sha1);
+   hmac_update(&ctx, U, sizeof(U));
+   hmac_final(&ctx, U);
for (k = 0; k < SHA1_RESULTLEN; k++)
result[k] ^= U[k];
}
@@ -94,7 +95,7 @@
 
 static const char *get_scram_server_final(struct scram_auth_request *request)
 {
-   struct hmac_sha1_context ctx;
+   struct hmac_context ctx;
const char *auth_message;
unsigned char server_key[SHA1_RESULTLEN];
unsigned char server_signature[SHA1_RESULTLEN];
@@ -104,17 +105,17 @@
request->server_first_message, ",",
request->client_final_message_without_proof, NULL);
 
-   hmac_sha1_init(&ctx, request->salted_password,
-  sizeof(request->salted_password));
-   hmac_sha1_update(&ctx, "Server Key", 10);
-   hmac_sha1_final(&ctx, server_key);
+   hmac_init(&ctx, request->salted_password,
+ sizeof(request->salted_password), &hash_method_sha1);
+   hmac_update(&ctx, "Server Key", 10);
+   hmac_final(&ctx, server_key);
 
safe_memset(request->salted_password, 0,
sizeof(request->salted_password));
 
-   hmac_sha1_init(&ctx, server_key, sizeof(server_key));
-   hmac_sha1_update(&ctx, auth_message, strlen(auth_message));
- 

Re: [Dovecot] [PATCH] Generalize HMAC implementation

2012-09-11 Thread Florian Zeitz
Am 11.09.2012 18:02, schrieb Timo Sirainen:
> On 4.9.2012, at 21.25, Florian Zeitz wrote:
> 
>> Hello everyone and Timo in particular,
>>
>> about a year ago I implemented a SHA-1 variant of the HMAC(-MD5) present
>> in Dovecot.
>> I had always disliked this a bit, because it replicates a lot of code.
>> This patch generalizes the HMAC function to take a hash_method struct as
>> parameter, and changes existing code which uses the "old" HMAC function
>> to use this new one.
>>
>> I'm not really sure this is actually a good idea, but I still felt I
>> should provide the code in case you would want to merge it upstream.
> 
> It's otherwise good, but this isn't safe:
> 
> + ctx->ctx = t_malloc(meth->context_size);
> + ctx->ctxo = t_malloc(meth->context_size);
> 
> It assumes that the hmac_init() + hmac_final() is called close to each others.
I had in fact noticed that. The assumption is currently true for all
occurrences, and probably will remain such, but I agree it's better to
be safe then sorry.

> I think we could simply #define the largest allowed context_size, use it for 
> these buffers' sizes and then add i_assert(meth->context_size <= 
> HMAC_MAX_CONTEXT_SIZE)
> 
Well, either that, or we could use a union of all known context structs
there. Possibly plus an
i_assert(meth->context_size <= sizeof(union hmac_ctxts)).
Or we could use i_malloc() and i_free() under the assumption hmac_init()
+ hmac_final() calls are always matched.

I've a certain preference for the union variant, but it's your call.

Regards,
Florian


Re: [Dovecot] ssl cert for mail server

2012-09-19 Thread Florian Zeitz
Am 19.09.2012 10:00, schrieb cc "maco" young:
> for testing a new ssl cert.  it works ok for browsers, but
> 
>>  openssl s_client -crlf -connect ms1.trailsandtribulations.net:443
> 
> => verify error:num=19:self signed certificate in certificate chain
> 
> is this ssl cert - as it's constructed - is ok for mail clients?  (realize
> needs to be on mail port etc - right now talking about the cert itself.)
>  have had problems with thunderbird, and was wondering if this might be
> part of the problem.
> 

Hi,

first of all this is likely off topic for this ML, I'll still answer
though, since I'm always intrigued by TLS problems.

The reason openssl doesn't accept this cert, while your browser does, is
quite likely that your system wide accepted CAs don't include Starfield
Technologies, while your browser's CAs do (This is the case for Firefox
and Thunderbird).

However, I suspect that your mail addresses are of the form
, and ms1.trailsandtribulations.net is
what is in your MX record. As such the certificate needs to be valid for
trailsandtribulations.net, and not ms1.trailsandtribulations.net.
So you either need trailsandtribulations.net as your CN, or a SAN of
type DNSName for trailsandtribulations.net.
Cf. https://tools.ietf.org/html/rfc6125 for best practices on generating
certificates.

Regards,
Florian


Re: [Dovecot] [PATCH] Generalize HMAC implementation

2012-09-19 Thread Florian Zeitz
Am 11.09.2012 20:21, schrieb Timo Sirainen:
> On 11.9.2012, at 21.07, Florian Zeitz wrote:
> 
>>> I think we could simply #define the largest allowed context_size, use it 
>>> for these buffers' sizes and then add i_assert(meth->context_size <= 
>>> HMAC_MAX_CONTEXT_SIZE)
>>>
>> Well, either that, or we could use a union of all known context structs
>> there.
> 
> That annoyingly requires #including in hmac.h all the different .h files that 
> contain hash contexts. When new hashes are added I doubt anyone remembers to 
> update that. And since the hmac_context is rather rarely used I don't think 
> it matters if we just use a somewhat large context size.
> 
>> Or we could use i_malloc() and i_free() under the assumption hmac_init()
>> + hmac_final() calls are always matched.
> 
> Yeah, that's not too bad either I guess. Although it is different from other 
> hash_init()+final calls which don't require it, and it's also wasting CPU a 
> bit.
> 
Here is an updated version of the patch.
It uses the variant defining the largest allowed content size.
I choose 1024 bit (128 Byte), since that is what most of the SHA-3
candidates use, so it's hopefully somewhat future proof.

It's also rebased to the current head and switches the hmac uses added
since the last version over to the changed API. It also fixes an
"interesting" compiler warning I got with clang, which complained about
the fact that struct hmac_context* was (re)declared in the prototype of
hmac_md5_get_cram_context() and therefore separate from the one declared
in hmac.h.

Regards,
Florian
# HG changeset patch
# User Florian Zeitz 
# Date 1346280236 -7200
# Node ID 0aa0a656176c9ca1c3f187999cd4725bf52665e5
# Parent  488c7dde19ad8baaa1d6bb48ee092c24eb9d44fb
lib: Generalize hmac to be hash independent

diff --git a/src/auth/auth-token.c b/src/auth/auth-token.c
--- a/src/auth/auth-token.c
+++ b/src/auth/auth-token.c
@@ -11,7 +11,8 @@
 
 #include "auth-common.h"
 #include "hex-binary.h"
-#include "hmac-sha1.h"
+#include "hmac.h"
+#include "sha1.h"
 #include "randgen.h"
 #include "read-full.h"
 #include "write-full.h"
@@ -168,16 +169,17 @@
 const char *auth_token_get(const char *service, const char *session_pid,
   const char *username, const char *session_id)
 {
-   struct hmac_sha1_context ctx;
+   struct hmac_context ctx;
unsigned char result[SHA1_RESULTLEN];
 
-   hmac_sha1_init(&ctx, username, strlen(username));
-   hmac_sha1_update(&ctx, session_pid, strlen(session_pid));
+   hmac_init(&ctx, (const unsigned char*)username, strlen(username),
+ &hash_method_sha1);
+   hmac_update(&ctx, session_pid, strlen(session_pid));
if (session_id != NULL && *session_id != '\0')
-   hmac_sha1_update(&ctx, session_id, strlen(session_id));
-   hmac_sha1_update(&ctx, service, strlen(service));
-   hmac_sha1_update(&ctx, auth_token_secret, sizeof(auth_token_secret));
-   hmac_sha1_final(&ctx, result);
+   hmac_update(&ctx, session_id, strlen(session_id));
+   hmac_update(&ctx, service, strlen(service));
+   hmac_update(&ctx, auth_token_secret, sizeof(auth_token_secret));
+   hmac_final(&ctx, result);
 
return binary_to_hex(result, sizeof(result));
 }
diff --git a/src/auth/mech-cram-md5.c b/src/auth/mech-cram-md5.c
--- a/src/auth/mech-cram-md5.c
+++ b/src/auth/mech-cram-md5.c
@@ -7,7 +7,9 @@
 #include "ioloop.h"
 #include "buffer.h"
 #include "hex-binary.h"
-#include "hmac-md5.h"
+#include "hmac-cram-md5.h"
+#include "hmac.h"
+#include "md5.h"
 #include "randgen.h"
 #include "mech.h"
 #include "passdb.h"
@@ -50,7 +52,7 @@
 {

unsigned char digest[MD5_RESULTLEN];
-struct hmac_md5_context ctx;
+struct hmac_context ctx;
const char *response_hex;
 
if (size != CRAM_MD5_CONTEXTLEN) {
@@ -59,9 +61,10 @@
return FALSE;
}
 
+   hmac_init(&ctx, NULL, 0, &hash_method_md5);
hmac_md5_set_cram_context(&ctx, credentials);
-   hmac_md5_update(&ctx, request->challenge, strlen(request->challenge));
-   hmac_md5_final(&ctx, digest);
+   hmac_update(&ctx, request->challenge, strlen(request->challenge));
+   hmac_final(&ctx, digest);
 
response_hex = binary_to_hex(digest, sizeof(digest));
 
diff --git a/src/auth/mech-scram-sha1.c b/src/auth/mech-scram-sha1.c
--- a/src/auth/mech-scram-sha1.c
+++ b/src/auth/mech-scram-sha1.c
@@ -9,7 +9,8 @@
 #include "auth-common.h"
 #include "base64.h"
 #inc

Re: [Dovecot] [PATCH] Generalize HMAC implementation

2012-09-19 Thread Florian Zeitz
Am 19.09.2012 19:43, schrieb Florian Zeitz:
> Am 11.09.2012 20:21, schrieb Timo Sirainen:
>> On 11.9.2012, at 21.07, Florian Zeitz wrote:
>>
>>>> I think we could simply #define the largest allowed context_size, use it 
>>>> for these buffers' sizes and then add i_assert(meth->context_size <= 
>>>> HMAC_MAX_CONTEXT_SIZE)
>>>>
>>> Well, either that, or we could use a union of all known context structs
>>> there.
>>
>> That annoyingly requires #including in hmac.h all the different .h files 
>> that contain hash contexts. When new hashes are added I doubt anyone 
>> remembers to update that. And since the hmac_context is rather rarely used I 
>> don't think it matters if we just use a somewhat large context size.
>>
>>> Or we could use i_malloc() and i_free() under the assumption hmac_init()
>>> + hmac_final() calls are always matched.
>>
>> Yeah, that's not too bad either I guess. Although it is different from other 
>> hash_init()+final calls which don't require it, and it's also wasting CPU a 
>> bit.
>>
> Here is an updated version of the patch.
> It uses the variant defining the largest allowed content size.
> I choose 1024 bit (128 Byte), since that is what most of the SHA-3
> candidates use, so it's hopefully somewhat future proof.

Forget I said that. Unfortunately the MD5 context is already bigger (the
SHA-1 one isn't). So 256 Byte it is I guess. Sorry for the lack of
thorough testing. I hope I didn't miss another hash function.

# HG changeset patch
# User Florian Zeitz 
# Date 1346280236 -7200
# Node ID 5e6802858a5bb3a9b278f1cbdabe5c0ffb15248d
# Parent  488c7dde19ad8baaa1d6bb48ee092c24eb9d44fb
lib: Generalize hmac to be hash independent

diff --git a/src/auth/auth-token.c b/src/auth/auth-token.c
--- a/src/auth/auth-token.c
+++ b/src/auth/auth-token.c
@@ -11,7 +11,8 @@
 
 #include "auth-common.h"
 #include "hex-binary.h"
-#include "hmac-sha1.h"
+#include "hmac.h"
+#include "sha1.h"
 #include "randgen.h"
 #include "read-full.h"
 #include "write-full.h"
@@ -168,16 +169,17 @@
 const char *auth_token_get(const char *service, const char *session_pid,
   const char *username, const char *session_id)
 {
-   struct hmac_sha1_context ctx;
+   struct hmac_context ctx;
unsigned char result[SHA1_RESULTLEN];
 
-   hmac_sha1_init(&ctx, username, strlen(username));
-   hmac_sha1_update(&ctx, session_pid, strlen(session_pid));
+   hmac_init(&ctx, (const unsigned char*)username, strlen(username),
+ &hash_method_sha1);
+   hmac_update(&ctx, session_pid, strlen(session_pid));
if (session_id != NULL && *session_id != '\0')
-   hmac_sha1_update(&ctx, session_id, strlen(session_id));
-   hmac_sha1_update(&ctx, service, strlen(service));
-   hmac_sha1_update(&ctx, auth_token_secret, sizeof(auth_token_secret));
-   hmac_sha1_final(&ctx, result);
+   hmac_update(&ctx, session_id, strlen(session_id));
+   hmac_update(&ctx, service, strlen(service));
+   hmac_update(&ctx, auth_token_secret, sizeof(auth_token_secret));
+   hmac_final(&ctx, result);
 
return binary_to_hex(result, sizeof(result));
 }
diff --git a/src/auth/mech-cram-md5.c b/src/auth/mech-cram-md5.c
--- a/src/auth/mech-cram-md5.c
+++ b/src/auth/mech-cram-md5.c
@@ -7,7 +7,9 @@
 #include "ioloop.h"
 #include "buffer.h"
 #include "hex-binary.h"
-#include "hmac-md5.h"
+#include "hmac-cram-md5.h"
+#include "hmac.h"
+#include "md5.h"
 #include "randgen.h"
 #include "mech.h"
 #include "passdb.h"
@@ -50,7 +52,7 @@
 {

unsigned char digest[MD5_RESULTLEN];
-struct hmac_md5_context ctx;
+struct hmac_context ctx;
const char *response_hex;
 
if (size != CRAM_MD5_CONTEXTLEN) {
@@ -59,9 +61,10 @@
return FALSE;
}
 
+   hmac_init(&ctx, NULL, 0, &hash_method_md5);
hmac_md5_set_cram_context(&ctx, credentials);
-   hmac_md5_update(&ctx, request->challenge, strlen(request->challenge));
-   hmac_md5_final(&ctx, digest);
+   hmac_update(&ctx, request->challenge, strlen(request->challenge));
+   hmac_final(&ctx, digest);
 
response_hex = binary_to_hex(digest, sizeof(digest));
 
diff --git a/src/auth/mech-scram-sha1.c b/src/auth/mech-scram-sha1.c
--- a/src/auth/mech-scram-sha1.c
+++ b/src/auth/mech-scram-sha1.c
@@ -9,7 +9,8 @@
 #include "auth-common.h"
 #include "base64.h"
 #include "buffer.h&qu

[Dovecot] [PATCH] Add SCRAM-SHA-1 password scheme

2012-10-02 Thread Florian Zeitz
Hello,

attached is an hg export on top of the current dovecot-2.2 branch, which
adds support for a SCRAM-SHA-1 password scheme.

Ideally I'd want doveadm pw's rounds flag to apply to this, but that's
currently specific to the crypt password scheme, so I left it out for now.

Regards,
Florian Zeitz
# HG changeset patch
# User Florian Zeitz 
# Date 1348017219 -7200
# Node ID 21a0d1b4daa7bb924f1666f0bb7c7e697a19c950
# Parent  8802322d72573ee17c52ce5e972e77e6f8ad69d1
auth: Add and use SCRAM-SHA-1 password scheme

diff --git a/src/auth/Makefile.am b/src/auth/Makefile.am
--- a/src/auth/Makefile.am
+++ b/src/auth/Makefile.am
@@ -44,6 +44,7 @@
password-scheme.c \
password-scheme-crypt.c \
password-scheme-md5crypt.c \
+   password-scheme-scram.c \
password-scheme-otp.c \
password-scheme-rpa.c
 
diff --git a/src/auth/mech-scram-sha1.c b/src/auth/mech-scram-sha1.c
--- a/src/auth/mech-scram-sha1.c
+++ b/src/auth/mech-scram-sha1.c
@@ -1,11 +1,13 @@
 /*
  * SCRAM-SHA-1 SASL authentication, see RFC-5802
  *
- * Copyright (c) 2011 Florian Zeitz 
+ * Copyright (c) 2011-2012 Florian Zeitz 
  *
  * This software is released under the MIT license.
  */
 
+#include 
+
 #include "auth-common.h"
 #include "base64.h"
 #include "buffer.h"
@@ -29,45 +31,22 @@
 
/* sent: */
const char *server_first_message;
-   unsigned char salt[16];
-   unsigned char salted_password[SHA1_RESULTLEN];
+   const char *snonce;
 
/* received: */
const char *gs2_cbind_flag;
const char *cnonce;
-   const char *snonce;
const char *client_first_message_bare;
const char *client_final_message_without_proof;
buffer_t *proof;
+
+   /* stored */
+   buffer_t *stored_key;
+   buffer_t *server_key;
 };
 
-static void Hi(const unsigned char *str, size_t str_size,
-  const unsigned char *salt, size_t salt_size, unsigned int i,
-  unsigned char result[SHA1_RESULTLEN])
-{
-   struct hmac_context ctx;
-   unsigned char U[SHA1_RESULTLEN];
-   unsigned int j, k;
-
-   /* Calculate U1 */
-   hmac_init(&ctx, str, str_size, &hash_method_sha1);
-   hmac_update(&ctx, salt, salt_size);
-   hmac_update(&ctx, "\0\0\0\1", 4);
-   hmac_final(&ctx, U);
-
-   memcpy(result, U, SHA1_RESULTLEN);
-
-   /* Calculate U2 to Ui and Hi */
-   for (j = 2; j <= i; j++) {
-   hmac_init(&ctx, str, str_size, &hash_method_sha1);
-   hmac_update(&ctx, U, sizeof(U));
-   hmac_final(&ctx, U);
-   for (k = 0; k < SHA1_RESULTLEN; k++)
-   result[k] ^= U[k];
-   }
-}
-
-static const char *get_scram_server_first(struct scram_auth_request *request)
+static const char *get_scram_server_first(struct scram_auth_request *request,
+ int iter, const char *salt)
 {
unsigned char snonce[SCRAM_SERVER_NONCE_LEN+1];
string_t *str;
@@ -84,12 +63,9 @@
snonce[sizeof(snonce)-1] = '\0';
request->snonce = p_strndup(request->pool, snonce, sizeof(snonce));
 
-   random_fill(request->salt, sizeof(request->salt));
-
-   str = t_str_new(MAX_BASE64_ENCODED_SIZE(sizeof(request->salt)));
-   str_printfa(str, "r=%s%s,s=", request->cnonce, request->snonce);
-   base64_encode(request->salt, sizeof(request->salt), str);
-   str_printfa(str, ",i=%d", SCRAM_ITERATE_COUNT);
+   str = t_str_new(sizeof(snonce));
+   str_printfa(str, "r=%s%s,s=%s,i=%d", request->cnonce, request->snonce,
+   salt, iter);
return str_c(str);
 }
 
@@ -105,15 +81,8 @@
request->server_first_message, ",",
request->client_final_message_without_proof, NULL);
 
-   hmac_init(&ctx, request->salted_password,
- sizeof(request->salted_password), &hash_method_sha1);
-   hmac_update(&ctx, "Server Key", 10);
-   hmac_final(&ctx, server_key);
-
-   safe_memset(request->salted_password, 0,
-   sizeof(request->salted_password));
-
-   hmac_init(&ctx, server_key, sizeof(server_key), &hash_method_sha1);
+   hmac_init(&ctx, request->server_key->data, request->server_key->used,
+ &hash_method_sha1);
hmac_update(&ctx, auth_message, strlen(auth_message));
hmac_final(&ctx, server_signature);
 
@@ -211,8 +180,7 @@
return TRUE;
 }
 
-static bool verify_credentials(struct scram_auth_request *request,
-  const unsigned char *credentials, size_t size)
+static bool verify_credentials(struct scram_auth_request *request)
 {
struct hmac_context ctx;
 

Re: [Dovecot] [PATCH] Add SCRAM-SHA-1 password scheme

2012-10-02 Thread Florian Zeitz
Am 02.10.2012 23:27, schrieb Timo Sirainen:
> On 3.10.2012, at 0.05, Florian Zeitz wrote:
> 
>> attached is an hg export on top of the current dovecot-2.2 branch, which
>> adds support for a SCRAM-SHA-1 password scheme.
>>
>> Ideally I'd want doveadm pw's rounds flag to apply to this, but that's
>> currently specific to the crypt password scheme, so I left it out for now.
> 
> Looks pretty good. But you could improve the error handling a bit. Instead of 
> atoi() use str_to_uint() and verify the error value. Also verify that 
> t_strsplit() returns the correct number of values. And there should be some 
> sanity check for the iter count also.. I'm not sure what, but currently it's 
> possible for Hi() to go to infinite loop.
> 
I shall. For the iteration count the endless loop should be fixed by
restricting the largest value to UINT_MAX-1, right? I'm not too fond of
stopping people from wasting their CPU time on Hi calculation beyond
this. I can try to guestimate a "sane" upper limit, but given time I
have an icky feeling that it will end up being too low. Thoughts?


Re: [Dovecot] [PATCH] Add SCRAM-SHA-1 password scheme

2012-10-02 Thread Florian Zeitz
Am 03.10.2012 01:42, schrieb Timo Sirainen:
> On 3.10.2012, at 0.05, Florian Zeitz wrote:
> 
>> attached is an hg export on top of the current dovecot-2.2 branch, which
>> adds support for a SCRAM-SHA-1 password scheme.
> 
> Oh, and SCRAM-SHA1 or SCRAM-SHA-1? I'd think SCRAM-SHA1 as the scheme is now 
> called, but elsewhere in the code (including user-visible strings) it says 
> SCRAM-SHA-1.
> 
Well, I usually prefer SCRAM-SHA-1, as that is how it is called in the
RFC, and SHA-1 is the hash name registered with IANA [1].
I did call the password scheme SCRAM-SHA1 to be consistent with other
current password schemes. I'm not 100% sure which one to use, or whether
a mix might even be the way to go ("correct" messages, but minimum user
confusion for password schemes).

[1]
https://www.iana.org/assignments/hash-function-text-names/hash-function-text-names.xml


Re: [Dovecot] [PATCH] Add SCRAM-SHA-1 password scheme

2012-10-02 Thread Florian Zeitz
Am 03.10.2012 01:58, schrieb Timo Sirainen:
> On 3.10.2012, at 2.54, Florian Zeitz wrote:
> 
>> Am 03.10.2012 01:42, schrieb Timo Sirainen:
>>> On 3.10.2012, at 0.05, Florian Zeitz wrote:
>>>
>>>> attached is an hg export on top of the current dovecot-2.2 branch, which
>>>> adds support for a SCRAM-SHA-1 password scheme.
>>>
>>> Oh, and SCRAM-SHA1 or SCRAM-SHA-1? I'd think SCRAM-SHA1 as the scheme is 
>>> now called, but elsewhere in the code (including user-visible strings) it 
>>> says SCRAM-SHA-1.
>>>
>> Well, I usually prefer SCRAM-SHA-1, as that is how it is called in the
>> RFC, and SHA-1 is the hash name registered with IANA [1].
>> I did call the password scheme SCRAM-SHA1 to be consistent with other
>> current password schemes. I'm not 100% sure which one to use, or whether
>> a mix might even be the way to go ("correct" messages, but minimum user
>> confusion for password schemes).
> 
> Hmm. Probably not worth it to have both SCRAM-SHA1 and SCRAM-SHA-1. And now I 
> see that the user-visible strings are about SCRAM-SHA-1 mechanism, not the 
> hash. So yeah, I guess the best way to avoid confusion is to call it 
> SCRAM-SHA-1 everywhere.
> 
Seems sensible.

Attached is a new export incorporating your feedback.
The iteration count is now limited to [4096, INT_MAX]. The lower bound
is a recommendation of the RFC.
# HG changeset patch
# User Florian Zeitz 
# Date 1348017219 -7200
# Node ID a0b0eece12335905500631477ec1d6ab31014469
# Parent  99843f74422ac68bfde86e9cee6920164eae4d5d
auth: Add and use SCRAM-SHA-1 password scheme

diff --git a/src/auth/Makefile.am b/src/auth/Makefile.am
--- a/src/auth/Makefile.am
+++ b/src/auth/Makefile.am
@@ -44,6 +44,7 @@
password-scheme.c \
password-scheme-crypt.c \
password-scheme-md5crypt.c \
+   password-scheme-scram.c \
password-scheme-otp.c \
password-scheme-rpa.c
 
diff --git a/src/auth/mech-scram-sha1.c b/src/auth/mech-scram-sha1.c
--- a/src/auth/mech-scram-sha1.c
+++ b/src/auth/mech-scram-sha1.c
@@ -1,11 +1,14 @@
 /*
  * SCRAM-SHA-1 SASL authentication, see RFC-5802
  *
- * Copyright (c) 2011 Florian Zeitz 
+ * Copyright (c) 2011-2012 Florian Zeitz 
  *
  * This software is released under the MIT license.
  */
 
+#include 
+#include 
+
 #include "auth-common.h"
 #include "base64.h"
 #include "buffer.h"
@@ -15,6 +18,7 @@
 #include "safe-memset.h"
 #include "str.h"
 #include "strfuncs.h"
+#include "strnum.h"
 #include "mech.h"
 
 /* SCRAM hash iteration count. RFC says it SHOULD be at least 4096 */
@@ -29,45 +33,22 @@
 
/* sent: */
const char *server_first_message;
-   unsigned char salt[16];
-   unsigned char salted_password[SHA1_RESULTLEN];
+   const char *snonce;
 
/* received: */
const char *gs2_cbind_flag;
const char *cnonce;
-   const char *snonce;
const char *client_first_message_bare;
const char *client_final_message_without_proof;
buffer_t *proof;
+
+   /* stored */
+   buffer_t *stored_key;
+   buffer_t *server_key;
 };
 
-static void Hi(const unsigned char *str, size_t str_size,
-  const unsigned char *salt, size_t salt_size, unsigned int i,
-  unsigned char result[SHA1_RESULTLEN])
-{
-   struct hmac_context ctx;
-   unsigned char U[SHA1_RESULTLEN];
-   unsigned int j, k;
-
-   /* Calculate U1 */
-   hmac_init(&ctx, str, str_size, &hash_method_sha1);
-   hmac_update(&ctx, salt, salt_size);
-   hmac_update(&ctx, "\0\0\0\1", 4);
-   hmac_final(&ctx, U);
-
-   memcpy(result, U, SHA1_RESULTLEN);
-
-   /* Calculate U2 to Ui and Hi */
-   for (j = 2; j <= i; j++) {
-   hmac_init(&ctx, str, str_size, &hash_method_sha1);
-   hmac_update(&ctx, U, sizeof(U));
-   hmac_final(&ctx, U);
-   for (k = 0; k < SHA1_RESULTLEN; k++)
-   result[k] ^= U[k];
-   }
-}
-
-static const char *get_scram_server_first(struct scram_auth_request *request)
+static const char *get_scram_server_first(struct scram_auth_request *request,
+ int iter, const char *salt)
 {
unsigned char snonce[SCRAM_SERVER_NONCE_LEN+1];
string_t *str;
@@ -84,12 +65,9 @@
snonce[sizeof(snonce)-1] = '\0';
request->snonce = p_strndup(request->pool, snonce, sizeof(snonce));
 
-   random_fill(request->salt, sizeof(request->salt));
-
-   str = t_str_new(MAX_BASE64_ENCODED_SIZE(sizeof(request->salt)));
-   str_printfa(str, "r=%s%s,s=", request->cnonce, request->snonce);
-   base64_encode(request->

Re: [Dovecot] [PATCH] Add SCRAM-SHA-1 password scheme

2012-10-03 Thread Florian Zeitz
Am 03.10.2012 02:50, schrieb Timo Sirainen:
> On Wed, 2012-10-03 at 02:10 +0200, Florian Zeitz wrote:
>> Attached is a new export incorporating your feedback.
> 
> Committed. Also what do you think about the attached patch? (Compiles,
> untested.)
> 
Moving the passdb parsing into a separate function seems like a nice
idea to me.
Style changes and removing an unused variable is obviously fine (I'm a
bit surprised I got no compiler warning about the latter, but oh well).

I did a quick test. Login and error checking seem to still work fine
with this patch in place. Wouldn't have seen anything in the code to
suggest otherwise either.


Re: [Dovecot] search and UTF-8 normalization forms (NFD)

2013-05-10 Thread Florian Zeitz
Am 02.05.2013 17:53, schrieb Timo Sirainen:
> On 25.4.2013, at 16.39, Lutz Preßler  wrote:
> 
>> on a system with dovecot 2.2 I've got a mailbox containing multiple mails
>> from a person called Krüger, but From: header encoded differently.
>> Some are encoded in UTF-8 normalization form decomposed (as used by Mac OSX),
>> that is u and umlaut accent as sperate combined codepoints
>> instead of one ü:
>>
>>  From: =?utf-8?Q?replaced_Kru=CC=88ger?= 
>>
>> Searching within roundcube webmail for "krüger" as sender
>> missis this mails.
>>
>> Roundcube sends (dovecot rawlog):
>> A0003 UID THREAD REFS UTF-8 ALL HEADER FROM {7+}krüger
>>
>> Is this supposed to work? Haven't done any more debugging
>> (other search variants) or read RFCs. As a user I would expect
>> Unicode equivalence rules be applied (see 
>> http://en.wikipedia.org/wiki/Unicode_equivalence)
> 
> IMAP requires using i;unicode-casemap by default, as specified by RFC 5051. 
> Then again, others could be supported as well, and it's not really a 
> requirement that the search can't handle more flexible searches.. Anyway, 
> that's what Dovecot currently has implemented, and I guess it doesn't do what 
> you want it to do. But there is a partial solution for this:
> 
> http://dovecot.org/patches/2.1/icu-1.2.tar.gz
> 
> It probably does what you want, but it only works with fts-lucene.
> 
Could you elaborate a bit why you think i;unicode-casemap does not
handle this case?

Is it only applied to the query, but not the header, or vice versa?
It seems to me that Step 2 should map both inputs to LATIN CAPITAL
LETTER U + COMBINING DIAERESIS.

Regards,
Florian


Re: [Dovecot] search and UTF-8 normalization forms (NFD)

2013-05-11 Thread Florian Zeitz
Am 10.05.2013 15:24, schrieb Florian Zeitz:
> Could you elaborate a bit why you think i;unicode-casemap does not
> handle this case?
> 
> Is it only applied to the query, but not the header, or vice versa?
> It seems to me that Step 2 should map both inputs to LATIN CAPITAL
> LETTER U + COMBINING DIAERESIS.
> 
> Regards,
> Florian
> 

So... I had a look at this. Turns out that the current implementation of
Unicode decomposition (Step 2(b) in i;unicode-casemap) in Dovecot is
broken. It only handles decomposition properties that include a tag.
I've attached a hg export that fixes this.
# HG changeset patch
# User Florian Zeitz 
# Date 1368284892 -7200
#  Sat May 11 17:08:12 2013 +0200
# Node ID 91f175781d9b75f1617ca5ba50dd58860ef0ae13
# Parent  62874b472dc6e5c30fe7fbc64c1bf868e08bf482
liblib: Fix Unicode decomposition

diff --git a/src/lib/test-unichar.c b/src/lib/test-unichar.c
--- a/src/lib/test-unichar.c
+++ b/src/lib/test-unichar.c
@@ -2,11 +2,15 @@
 
 #include "test-lib.h"
 #include "str.h"
+#include "buffer.h"
 #include "unichar.h"
 
 void test_unichar(void)
 {
-   static const char *overlong_utf8 = "\xf8\x80\x95\x81\xa1";
+   static const char overlong_utf8[] = "\xf8\x80\x95\x81\xa1";
+   static const char collate_in[] = "\xc3\xbc \xc2\xb3";
+   static const char collate_exp[] = "U\xcc\x88 3";
+   buffer_t *collate_out;
unichar_t chr, chr2;
string_t *str = t_str_new(16);
 
@@ -18,6 +22,13 @@
test_assert(uni_utf8_get_char(str_c(str), &chr2) > 0);
test_assert(chr2 == chr);
}
+
+   collate_out = buffer_create_dynamic(default_pool, 32);
+   uni_utf8_to_decomposed_titlecase(collate_in, sizeof(collate_in),
+collate_out);
+   test_assert(!strcmp(collate_out->data, collate_exp));
+   buffer_free(&collate_out);
+
test_assert(!uni_utf8_str_is_valid(overlong_utf8));
test_assert(uni_utf8_get_char(overlong_utf8, &chr2) < 0);
test_end();
diff --git a/src/lib/unichar.c b/src/lib/unichar.c
--- a/src/lib/unichar.c
+++ b/src/lib/unichar.c
@@ -287,7 +287,7 @@
 
 static bool uni_ucs4_decompose_multi_utf8(unichar_t chr, buffer_t *output)
 {
-   const uint16_t *value;
+   const uint32_t *value;
unsigned int idx;
 
if (chr < multidecomp_keys[0] || chr > 0x)
diff --git a/src/lib/unicodemap.pl b/src/lib/unicodemap.pl
--- a/src/lib/unicodemap.pl
+++ b/src/lib/unicodemap.pl
@@ -30,14 +30,14 @@
   push @titlecase32_keys, $code;
   push @titlecase32_values, $value;
 }
-  } elsif ($decomp =~ /\<[^>]*> (.+)/) {
+  } elsif ($decomp =~ /(?:\<[^>]*> )?(.+)/) {
 # decompositions
 my $decomp_codes = $1;
 if ($decomp_codes =~ /^([0-9A-Z]*)$/i) {
   # unicharacter decomposition. use separate lists for this
   my $value = eval("0x$1");
-  if ($value > 0x) {
-   print STDERR "Error: We've assumed decomposition codes are max. 
16bit\n";
+  if ($value > 0x) {
+   print STDERR "Error: We've assumed decomposition codes are max. 
32bit\n";
exit 1;
   }
   if ($code <= 0xff) {
@@ -61,8 +61,8 @@
 
   foreach my $dcode (split(" ", $decomp_codes)) {
my $value = eval("0x$dcode");
-   if ($value > 0x) {
- print STDERR "Error: We've assumed decomposition codes are max. 
16bit\n";
+   if ($value > 0x) {
+ print STDERR "Error: We've assumed decomposition codes are max. 
32bit\n";
  exit 1;
}
push @multidecomp_values, $value;
@@ -78,7 +78,7 @@
   my $last = $#list;
   my $n = 0;
   foreach my $key (@list) {
-printf("0x%04x", $key);
+printf("0x%05x", $key);
 last if ($n == $last);
 print ",";
 
@@ -137,7 +137,7 @@
 print_list(\@uni16_decomp_keys);
 print "\n};\n";
 
-print "static const uint16_t uni16_decomp_values[] = {\n\t";
+print "static const uint32_t uni16_decomp_values[] = {\n\t";
 print_list(\@uni16_decomp_values);
 print "\n};\n";
 
@@ -145,7 +145,7 @@
 print_list(\@uni32_decomp_keys);
 print "\n};\n";
 
-print "static const uint16_t uni32_decomp_values[] = {\n\t";
+print "static const uint32_t uni32_decomp_values[] = {\n\t";
 print_list(\@uni32_decomp_values);
 print "\n};\n";
 
@@ -157,6 +157,6 @@
 print_list(\@multidecomp_offsets);
 print "\n};\n";
 
-print "static const uint16_t multidecomp_values[] = {\n\t";
+print "static const uint32_t multidecomp_values[] = {\n\t";
 print_list(\@multidecomp_values);
 print "\n};\n";