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