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