Merge remote-tracking branch 'origin/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 static int do_auth_get_cram(const char *authtype, char *authdata,
83 struct cram_callback_info *craminfo, int logerr)
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 if (logerr)
94 {
95 DPRINTF("Unsupported authentication type: %s", authtype);
96 }
97 errno=EPERM;
98 return (-1);
99 }
100
101 for (i=0; hmac_list[i]; i++)
102 if (strcmp(hmac_list[i]->hh_name, authtype+5) == 0)
103 break;
104
105 if (logerr)
106 {
107 DPRINTF("cram: challenge=%s, response=%s", craminfo->challenge,
108 craminfo->response);
109 }
110
111 if (hmac_list[i] == 0
112 || (challenge_l=authsasl_frombase64(craminfo->challenge)) < 0
113 || (response_l=authsasl_frombase64(craminfo->response)) < 0)
114 {
115 if (logerr)
116 {
117 DPRINTF("cram: invalid base64 encoding, or unknown method: %s",
118 authtype);
119 }
120 errno=EACCES;
121 return (-1);
122 }
123 craminfo->h=hmac_list[i];
124
125 for (i=response_l; i > 0; )
126 {
127 if (craminfo->response[i-1] == ' ')
128 break;
129 --i;
130 }
131
132 if (i == 0)
133 {
134 if (logerr)
135 {
136 DPRINTF("cram: invalid base64 encoding");
137 }
138 errno=EACCES;
139 return (-1);
140 }
141 craminfo->response[i-1]=0;
142 craminfo->user = craminfo->response;
143 craminfo->response += i;
144 response_l -= i;
145
146 /* Since base64decoded data is always lesser in size (at least),
147 ** we can do the following:
148 */
149 craminfo->challenge[challenge_l]=0;
150 craminfo->response[response_l]=0;
151
152 if (logerr)
153 {
154 /* we rely on DPRINTF doing a "safe" print here */
155 DPRINTF("cram: decoded challenge/response, username '%s'",
156 craminfo->user);
157 }
158 return (0);
159 }
160
161 int auth_cram_callback(struct authinfo *a, void *vp)
162 {
163 struct cram_callback_info *cci=(struct cram_callback_info *)vp;
164 unsigned char *hashbuf;
165 unsigned char *p;
166 unsigned i;
167 static const char hex[]="0123456789abcdef";
168 int rc;
169
170 if (!a->clearpasswd)
171 return (-1);
172
173 /*
174 hmac->hh_L*2 will be the size of the binary hash.
175
176 hmac->hh_L*4+1 will therefore be size of the binary hash,
177 as a hexadecimal string.
178 */
179
180 if ((hashbuf=malloc(cci->h->hh_L*6+1)) == 0)
181 return (1);
182
183 hmac_hashkey(cci->h, a->clearpasswd, strlen(a->clearpasswd),
184 hashbuf, hashbuf+cci->h->hh_L);
185
186 p=hashbuf+cci->h->hh_L*2;
187
188 for (i=0; i<cci->h->hh_L*2; i++)
189 {
190 char c;
191
192 c = hex[ (hashbuf[i] >> 4) & 0x0F];
193 *p++=c;
194
195 c = hex[ hashbuf[i] & 0x0F];
196 *p++=c;
197
198 *p=0;
199 }
200
201 rc=auth_verify_cram(cci->h, cci->challenge, cci->response,
202 (const char *)hashbuf+cci->h->hh_L*2);
203 free(hashbuf);
204
205 if (rc) return (rc);
206
207 return (*cci->callback_func)(a, cci->callback_arg);
208 }
209
210 int auth_get_cram(const char *authtype, char *authdata,
211 struct cram_callback_info *craminfo)
212 {
213 return do_auth_get_cram(authtype, authdata, craminfo, 1);
214 }
215
216 int auth_get_cram_silent(const char *authtype, char *authdata,
217 struct cram_callback_info *craminfo)
218 {
219 return do_auth_get_cram(authtype, authdata, craminfo, 0);
220 }