Import Upstream version 0.69.0
[hcoop/debian/courier-authlib.git] / cramlib.c
CommitLineData
d9898ee8 1/*
ac40fd9e 2** Copyright 1998 - 2008 Double Precision, Inc. See COPYING for
d9898ee8 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"
0e333c05 14#include "courierauth.h"
d9898ee8 15#include "courierauthdebug.h"
d9898ee8 16#include "libhmac/hmac.h"
d9898ee8 17
18static 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
26static int do_auth_verify_cram(struct hmac_hashinfo *hash,
27 const char *challenge, const char *response,
28 const char *hashsecret)
29{
30unsigned char *context;
31unsigned 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
70int auth_verify_cram(struct hmac_hashinfo *hash,
71 const char *challenge, const char *response,
72 const char *hashsecret)
73{
74int 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
b0322a85
CE
81static int do_auth_get_cram(const char *authtype, char *authdata,
82 struct cram_callback_info *craminfo, int logerr)
d9898ee8 83{
84int i;
85int challenge_l;
86int response_l;
87
88 if (strncmp(authtype, "cram-", 5) ||
89 (craminfo->challenge=strtok(authdata, "\n")) == 0 ||
90 (craminfo->response=strtok(0, "\n")) == 0)
91 {
b0322a85
CE
92 if (logerr)
93 {
94 DPRINTF("Unsupported authentication type: %s", authtype);
95 }
d9898ee8 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
b0322a85
CE
104 if (logerr)
105 {
106 DPRINTF("cram: challenge=%s, response=%s", craminfo->challenge,
107 craminfo->response);
108 }
d9898ee8 109
110 if (hmac_list[i] == 0
111 || (challenge_l=authsasl_frombase64(craminfo->challenge)) < 0
112 || (response_l=authsasl_frombase64(craminfo->response)) < 0)
113 {
b0322a85
CE
114 if (logerr)
115 {
116 DPRINTF("cram: invalid base64 encoding, or unknown method: %s",
117 authtype);
118 }
d9898ee8 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 {
b0322a85
CE
133 if (logerr)
134 {
135 DPRINTF("cram: invalid base64 encoding");
136 }
d9898ee8 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
b0322a85
CE
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 }
d9898ee8 157 return (0);
158}
159
160int auth_cram_callback(struct authinfo *a, void *vp)
161{
162struct cram_callback_info *cci=(struct cram_callback_info *)vp;
163unsigned char *hashbuf;
164unsigned char *p;
165unsigned i;
166static const char hex[]="0123456789abcdef";
167int 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}
b0322a85
CE
208
209int 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
215int 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}