Merge branch 'upstream' into debian
[hcoop/debian/courier-authlib.git] / cramlib.c
1 /*
2 ** Copyright 1998 - 2008 Double Precision, Inc. See COPYING for
3 ** distribution information.
4 */
5
6 #if HAVE_CONFIG_H
7 #include "courier_auth_config.h"
8 #endif
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <errno.h>
13 #include "courierauthsasl.h"
14 #include "cramlib.h"
15 #include "courierauthdebug.h"
16 #include "libhmac/hmac.h"
17 #include "cramlib.h"
18
19 static int nybble(int c)
20 {
21 if (c >= '0' && c <= '9') return (c-'0');
22 if (c >= 'a' && c <= 'f') return (c-'a'+10);
23 if (c >= 'A' && c <= 'F') return (c-'A'+10);
24 return (-1);
25 }
26
27 static int do_auth_verify_cram(struct hmac_hashinfo *hash,
28 const char *challenge, const char *response,
29 const char *hashsecret)
30 {
31 unsigned char *context;
32 unsigned i;
33
34 if (strlen(hashsecret) != hash->hh_L*4 ||
35 strlen(response) != hash->hh_L*2)
36 return (-1);
37
38 if ((context=malloc(hash->hh_L*3)) == 0) return (-1);
39
40 for (i=0; i<hash->hh_L*2; i++)
41 {
42 int a=nybble(hashsecret[i*2]), b=nybble(hashsecret[i*2+1]);
43
44 if (a < 0 || b < 0)
45 {
46 free(context);
47 return (-1);
48 }
49 context[i]= a*16 + b;
50 }
51
52 hmac_hashtext(hash, challenge, strlen(challenge),
53 context, context+hash->hh_L,
54 context+hash->hh_L*2);
55
56 for (i=0; i<hash->hh_L; i++)
57 {
58 int a=nybble(response[i*2]), b=nybble(response[i*2+1]);
59
60 if ( (unsigned char)(a*16+b) !=
61 context[hash->hh_L*2+i])
62 {
63 free(context);
64 return (-1);
65 }
66 }
67 free(context);
68 return (0);
69 }
70
71 int auth_verify_cram(struct hmac_hashinfo *hash,
72 const char *challenge, const char *response,
73 const char *hashsecret)
74 {
75 int rc;
76
77 rc = do_auth_verify_cram(hash, challenge, response, hashsecret);
78 DPRINTF(rc ? "cram validation failed" : "cram validation succeeded");
79 return rc;
80 }
81
82 int auth_get_cram(const char *authtype, char *authdata,
83 struct cram_callback_info *craminfo)
84 {
85 int i;
86 int challenge_l;
87 int response_l;
88
89 if (strncmp(authtype, "cram-", 5) ||
90 (craminfo->challenge=strtok(authdata, "\n")) == 0 ||
91 (craminfo->response=strtok(0, "\n")) == 0)
92 {
93 DPRINTF("Unsupported authentication type: %s", authtype);
94 errno=EPERM;
95 return (-1);
96 }
97
98 for (i=0; hmac_list[i]; i++)
99 if (strcmp(hmac_list[i]->hh_name, authtype+5) == 0)
100 break;
101
102 DPRINTF("cram: challenge=%s, response=%s", craminfo->challenge,
103 craminfo->response);
104
105 if (hmac_list[i] == 0
106 || (challenge_l=authsasl_frombase64(craminfo->challenge)) < 0
107 || (response_l=authsasl_frombase64(craminfo->response)) < 0)
108 {
109 DPRINTF("cram: invalid base64 encoding, or unknown method: %s",
110 authtype);
111 errno=EACCES;
112 return (-1);
113 }
114 craminfo->h=hmac_list[i];
115
116 for (i=response_l; i > 0; )
117 {
118 if (craminfo->response[i-1] == ' ')
119 break;
120 --i;
121 }
122
123 if (i == 0)
124 {
125 DPRINTF("cram: invalid base64 encoding");
126 errno=EACCES;
127 return (-1);
128 }
129 craminfo->response[i-1]=0;
130 craminfo->user = craminfo->response;
131 craminfo->response += i;
132 response_l -= i;
133
134 /* Since base64decoded data is always lesser in size (at least),
135 ** we can do the following:
136 */
137 craminfo->challenge[challenge_l]=0;
138 craminfo->response[response_l]=0;
139
140 /* we rely on DPRINTF doing a "safe" print here */
141 DPRINTF("cram: decoded challenge/response, username '%s'",
142 craminfo->user);
143 return (0);
144 }
145
146 int auth_cram_callback(struct authinfo *a, void *vp)
147 {
148 struct cram_callback_info *cci=(struct cram_callback_info *)vp;
149 unsigned char *hashbuf;
150 unsigned char *p;
151 unsigned i;
152 static const char hex[]="0123456789abcdef";
153 int rc;
154
155 if (!a->clearpasswd)
156 return (-1);
157
158 /*
159 hmac->hh_L*2 will be the size of the binary hash.
160
161 hmac->hh_L*4+1 will therefore be size of the binary hash,
162 as a hexadecimal string.
163 */
164
165 if ((hashbuf=malloc(cci->h->hh_L*6+1)) == 0)
166 return (1);
167
168 hmac_hashkey(cci->h, a->clearpasswd, strlen(a->clearpasswd),
169 hashbuf, hashbuf+cci->h->hh_L);
170
171 p=hashbuf+cci->h->hh_L*2;
172
173 for (i=0; i<cci->h->hh_L*2; i++)
174 {
175 char c;
176
177 c = hex[ (hashbuf[i] >> 4) & 0x0F];
178 *p++=c;
179
180 c = hex[ hashbuf[i] & 0x0F];
181 *p++=c;
182
183 *p=0;
184 }
185
186 rc=auth_verify_cram(cci->h, cci->challenge, cci->response,
187 (const char *)hashbuf+cci->h->hh_L*2);
188 free(hashbuf);
189
190 if (rc) return (rc);
191
192 return (*cci->callback_func)(a, cci->callback_arg);
193 }