1 #include "auth.h"
2 #include "base64.h"
3 #include "buf.h"
4 #include "str.h"
5 #include "unused.h"
6 
7 #include <vanessa_logger.h>
8 
9 #if WITH_LIBIDN
10 #include <stringprep.h>
11 
saslprep(const char * in)12 static char *saslprep(const char *in)
13 {
14 	int status;
15 	char *out;
16 
17 	status = stringprep_profile(in, &out, "SASLprep", 0);
18 	if (status != STRINGPREP_OK) {
19 		VANESSA_LOGGER_DEBUG_UNSAFE("stringprep_profile: \"%s\"",
20 					    stringprep_strerror(status));
21 		return NULL;
22 	}
23 
24 	return out;
25 }
26 
saslprep_str_free(char * out)27 static void saslprep_str_free(char *out)
28 {
29 	free(out);
30 }
31 
32 #else
saslprep(char * in)33 static char *saslprep(char *in)
34 {
35 	return in;
36 }
37 
saslprep_str_free(char * UNUSED (out))38 static void saslprep_str_free(char *UNUSED(out))
39 {
40 	;
41 }
42 #endif
43 
44 /**********************************************************************
45  * sasl_plain_challenge_decode
46  * Decode a SASL PLAIN challenge
47  * pre: challenge: the challenge
48  * return: .auth: seeded auth structure
49  *         .status: auth_status_ok on success
50  *                  auth_status_invalid if the challenge is invalid
51  *                  auth_status_error on internal error
52  **********************************************************************/
53 
sasl_plain_challenge_decode(char * challenge)54 struct auth_status sasl_plain_challenge_decode(char *challenge)
55 {
56 	STRUCT_CONST_BUF(in);
57 	STRUCT_BUF(out);
58 	STRUCT_AUTH_STATUS(as);
59 	char *authorisation_id = NULL, *authentication_id = NULL;
60 	char *passwd = NULL, *base64_data = NULL, *tmp = NULL;
61 
62 	in.data = challenge;
63 	in.len = strlen(challenge);
64 	out = base64_decode(&in);
65 	if (buf_is_err(&out)) {
66 		if (out.len) {
67 			VANESSA_LOGGER_DEBUG("base64_decode");
68 			goto err;
69 		}
70 		as.reason = "Invalid base64 encoded challenge, mate";
71 		as.status = auth_status_invalid;
72 		goto err;
73 	}
74 	base64_data = out.data;
75 
76 	/* Note about the use of strn_to_str() below.
77 	 *
78 	 * This may over-allocate and result in a string with
79 	 * more than one '\0', but it won't over-run and anything
80 	 * after the first '\0' will subsequently be ignored */
81 
82 	if (out.len == 0) {
83 		as.reason = "Empty challenge, mate";
84 		as.status = auth_status_invalid;
85 		goto err;
86 	}
87 
88 	if (*out.data) {
89 		char *saslprep_str;
90 
91 		authorisation_id = strn_to_str(out.data, out.len);
92 		if (!authorisation_id) {
93 			VANESSA_LOGGER_DEBUG("strdup authorisation_id");
94 			goto err;
95 		}
96 
97 		saslprep_str = authorisation_id;
98 		authorisation_id = saslprep(authorisation_id);
99 		saslprep_str_free(saslprep_str);
100 		if (!authorisation_id) {
101 			VANESSA_LOGGER_DEBUG("saslprep "
102 					     "authorisation_id");
103 			goto err;
104 		}
105 
106 		out.data += strlen(authorisation_id);
107 		out.len -= strlen(authorisation_id);
108 	}
109 
110 	if (out.len < 1) {
111 		as.reason = "Challenge has no authentication id, mate";
112 		as.status = -1;
113 		goto err;
114 	}
115 
116 	out.data++;
117 	out.len--;
118 
119 	authentication_id = strn_to_str(out.data, out.len);
120 	if (!authentication_id) {
121 		VANESSA_LOGGER_DEBUG("strdup authentication_id");
122 		goto err;
123 	}
124 	out.data += strlen(authentication_id);
125 	out.len -= strlen(authentication_id);
126 
127 	tmp = saslprep(authentication_id);
128 	if (!tmp) {
129 		VANESSA_LOGGER_DEBUG("saslprep: authentication_id");
130 		goto err;
131 	}
132 	if (tmp != authentication_id) {
133 		free(authentication_id);
134 		authentication_id = tmp;
135 	}
136 
137 	if (!*authentication_id) {
138 		as.reason = "Empty authentication id, mate";
139 		as.status = auth_status_invalid;
140 		goto err;
141 	}
142 
143 	if (out.len < 1) {
144 		as.reason = "Challenge has no password, mate";
145 		as.status = auth_status_invalid;
146 		goto err;
147 	}
148 
149 	out.data++;
150 	out.len--;
151 
152 	passwd = strn_to_str(out.data, out.len);
153 	if (!passwd) {
154 		VANESSA_LOGGER_DEBUG("strdup passwd");
155 		goto err;
156 	}
157 	out.len -= strlen(passwd);
158 
159 	if (out.len) {
160 		as.reason = "Trailing garbage in challenge, mate";
161 		as.status = -1;
162 		goto err;
163 	}
164 
165 	as.auth = auth_set_sasl_plain(authorisation_id,
166 				      authentication_id, passwd);
167 	authorisation_id = authentication_id = passwd = NULL;
168 	as.status = auth_status_ok;
169 
170 err:
171 	free(authorisation_id);
172 	free(authentication_id);
173 	free(passwd);
174 	free(base64_data);
175 	return as;
176 }
177 
178 /**********************************************************************
179  * sasl_plain_challenge_encode
180  * Encode a SASL PLAIN challenge
181  * pre: auth: seeded auth structure
182  * return: encoded challenge
183  *         NULL on error
184  **********************************************************************/
185 
sasl_plain_challenge_encode(const struct auth * auth)186 char * sasl_plain_challenge_encode(const struct auth *auth)
187 {
188 	STRUCT_BUF(in);
189 	STRUCT_BUF(out);
190 	char *p, *out_str = NULL;
191 
192 	if (auth->authorisation_id)
193 		in.len += strlen(auth->authorisation_id);
194 	in.len += 1 + strlen(auth->authentication_id) + 1;
195 	in.len += strlen(auth->passwd);
196 
197 	in.data = malloc(in.len);
198 	if (!in.len) {
199 		VANESSA_LOGGER_DEBUG_ERRNO("malloc");
200 		goto err;
201 	}
202 
203 	p = in.data;
204 	if (auth->authorisation_id) {
205 		strcpy(in.data, auth->authorisation_id);
206 		p += strlen(auth->authorisation_id) + 1;
207 	} else {
208 		in.data[0] = '\0';
209 		p++;
210 	}
211 	strcpy(p, auth->authentication_id);
212 	p += strlen(auth->authentication_id) + 1;
213 	memcpy(p, auth->passwd, strlen(auth->passwd));
214 
215 	out = base64_encode(&in);
216 	if (buf_is_err(&out)) {
217 		VANESSA_LOGGER_DEBUG("base64_encode");
218 		goto err;
219 	}
220 
221 	out_str = strn_to_str(out.data, out.len);
222 	if (!out_str) {
223 		VANESSA_LOGGER_DEBUG("strn_to_str");
224 		goto err;
225 	}
226 
227 err:
228 	free(out.data);
229 	free(in.data);
230 	return out_str;
231 }
232