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 
nybble(int c)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 
do_auth_verify_cram(struct hmac_hashinfo * hash,const char * challenge,const char * response,const char * hashsecret)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 
auth_verify_cram(struct hmac_hashinfo * hash,const char * challenge,const char * response,const char * hashsecret)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 
do_auth_get_cram(const char * authtype,char * authdata,struct cram_callback_info * craminfo,int logerr)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 
auth_cram_callback(struct authinfo * a,void * vp)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 
auth_get_cram(const char * authtype,char * authdata,struct cram_callback_info * craminfo)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 
auth_get_cram_silent(const char * authtype,char * authdata,struct cram_callback_info * craminfo)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 }
220