stropheruby 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +9 -0
- data/History.txt +4 -0
- data/Manifest.txt +40 -0
- data/PostInstall.txt +4 -0
- data/README.txt +28 -0
- data/Rakefile +21 -0
- data/examples/xmpp_client.rb +77 -0
- data/ext/auth.c +990 -0
- data/ext/common.h +287 -0
- data/ext/conn.c +611 -0
- data/ext/ctx.c +416 -0
- data/ext/event.c +351 -0
- data/ext/extconf.rb +8 -0
- data/ext/handler.c +592 -0
- data/ext/hash.c +278 -0
- data/ext/hash.h +64 -0
- data/ext/jid.c +177 -0
- data/ext/md5.c +289 -0
- data/ext/md5.h +41 -0
- data/ext/ostypes.h +27 -0
- data/ext/parser.c +208 -0
- data/ext/sasl.c +614 -0
- data/ext/sasl.h +44 -0
- data/ext/sha1.c +389 -0
- data/ext/sha1.h +31 -0
- data/ext/snprintf.c +839 -0
- data/ext/sock.c +911 -0
- data/ext/sock.h +51 -0
- data/ext/stanza.c +908 -0
- data/ext/strophe.h +372 -0
- data/ext/strophe_ruby.c +721 -0
- data/ext/thread.c +119 -0
- data/ext/thread.h +43 -0
- data/ext/tls.h +46 -0
- data/ext/tls_dummy.c +89 -0
- data/ext/util.c +107 -0
- data/ext/util.h +32 -0
- data/lib/strophe_ruby.rb +6 -0
- data/test/test_helper.rb +3 -0
- data/test/test_strophe_ruby.rb +11 -0
- data/test/test_strophe_ruby_extn.rb +9 -0
- metadata +111 -0
data/ext/sasl.c
ADDED
@@ -0,0 +1,614 @@
|
|
1
|
+
/* sasl.c
|
2
|
+
** strophe XMPP client library -- SASL authentication helpers
|
3
|
+
**
|
4
|
+
** Copyright (C) 2005-2008 OGG, LLC. All rights reserved.
|
5
|
+
**
|
6
|
+
** This software is provided AS-IS with no warranty, either express
|
7
|
+
** or implied.
|
8
|
+
**
|
9
|
+
** This software is distributed under license and may not be copied,
|
10
|
+
** modified or distributed except as expressly authorized under the
|
11
|
+
** terms of the license contained in the file LICENSE.txt in this
|
12
|
+
** distribution.
|
13
|
+
*/
|
14
|
+
|
15
|
+
/** @file
|
16
|
+
* SASL authentication.
|
17
|
+
*/
|
18
|
+
|
19
|
+
#include <string.h>
|
20
|
+
|
21
|
+
#include "strophe.h"
|
22
|
+
#include "common.h"
|
23
|
+
#include "sasl.h"
|
24
|
+
#include "md5.h"
|
25
|
+
|
26
|
+
/* make sure the stdint.h types are available */
|
27
|
+
#if defined(_MSC_VER) /* Microsoft Visual C++ */
|
28
|
+
typedef signed char int8_t;
|
29
|
+
typedef short int int16_t;
|
30
|
+
typedef int int32_t;
|
31
|
+
typedef __int64 int64_t;
|
32
|
+
|
33
|
+
typedef unsigned char uint8_t;
|
34
|
+
typedef unsigned short int uint16_t;
|
35
|
+
typedef unsigned int uint32_t;
|
36
|
+
/* no uint64_t */
|
37
|
+
#else
|
38
|
+
#include <stdint.h>
|
39
|
+
#endif
|
40
|
+
|
41
|
+
|
42
|
+
/** generate authentication string for the SASL PLAIN mechanism */
|
43
|
+
char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password) {
|
44
|
+
int idlen, passlen;
|
45
|
+
char *result = NULL;
|
46
|
+
char *msg;
|
47
|
+
|
48
|
+
/* our message is Base64(authzid,\0,authid,\0,password)
|
49
|
+
if there is no authzid, that field is left empty */
|
50
|
+
|
51
|
+
idlen = strlen(authid);
|
52
|
+
passlen = strlen(password);
|
53
|
+
msg = xmpp_alloc(ctx, 2 + idlen + passlen);
|
54
|
+
if (msg != NULL) {
|
55
|
+
msg[0] = '\0';
|
56
|
+
memcpy(msg+1, authid, idlen);
|
57
|
+
msg[1+idlen] = '\0';
|
58
|
+
memcpy(msg+1+idlen+1, password, passlen);
|
59
|
+
result = base64_encode(ctx, (unsigned char *)msg, 2 + idlen + passlen);
|
60
|
+
xmpp_free(ctx, msg);
|
61
|
+
}
|
62
|
+
|
63
|
+
return result;
|
64
|
+
}
|
65
|
+
|
66
|
+
/** helpers for digest auth */
|
67
|
+
|
68
|
+
/* create a new, null-terminated string from a substring */
|
69
|
+
static char *_make_string(xmpp_ctx_t *ctx, const char *s, const unsigned len)
|
70
|
+
{
|
71
|
+
char *result;
|
72
|
+
|
73
|
+
result = xmpp_alloc(ctx, len + 1);
|
74
|
+
if (result != NULL) {
|
75
|
+
memcpy(result, s, len);
|
76
|
+
result[len] = '\0';
|
77
|
+
}
|
78
|
+
return result;
|
79
|
+
}
|
80
|
+
|
81
|
+
/* create a new, null-terminated string quoting another string */
|
82
|
+
static char *_make_quoted(xmpp_ctx_t *ctx, const char *s)
|
83
|
+
{
|
84
|
+
char *result;
|
85
|
+
int len = strlen(s);
|
86
|
+
|
87
|
+
result = xmpp_alloc(ctx, len + 3);
|
88
|
+
if (result != NULL) {
|
89
|
+
result[0] = '"';
|
90
|
+
memcpy(result+1, s, len);
|
91
|
+
result[len+1] = '"';
|
92
|
+
result[len+2] = '\0';
|
93
|
+
}
|
94
|
+
return result;
|
95
|
+
}
|
96
|
+
|
97
|
+
/* split key, value pairs into a hash */
|
98
|
+
static hash_t *_parse_digest_challenge(xmpp_ctx_t *ctx, const char *msg)
|
99
|
+
{
|
100
|
+
hash_t *result;
|
101
|
+
unsigned char *text;
|
102
|
+
char *key, *value;
|
103
|
+
unsigned char *s, *t;
|
104
|
+
|
105
|
+
text = base64_decode(ctx, msg, strlen(msg));
|
106
|
+
if (text == NULL) {
|
107
|
+
xmpp_error(ctx, "SASL", "couldn't Base64 decode challenge!");
|
108
|
+
return NULL;
|
109
|
+
}
|
110
|
+
|
111
|
+
result = hash_new(ctx, 10, xmpp_free);
|
112
|
+
if (result != NULL) {
|
113
|
+
s = text;
|
114
|
+
while (*s != '\0') {
|
115
|
+
/* skip any leading commas and spaces */
|
116
|
+
while ((*s == ',') || (*s == ' ')) s++;
|
117
|
+
/* accumulate a key ending at '=' */
|
118
|
+
t = s;
|
119
|
+
while ((*t != '=') && (*t != '\0')) t++;
|
120
|
+
if (*t == '\0') break; /* bad string */
|
121
|
+
key = _make_string(ctx, (char *)s, (t-s));
|
122
|
+
if (key == NULL) break;
|
123
|
+
/* advance our start pointer past the key */
|
124
|
+
s = t + 1;
|
125
|
+
t = s;
|
126
|
+
/* if we see quotes, grab the string in between */
|
127
|
+
if ((*s == '\'') || (*s == '"')) {
|
128
|
+
t++;
|
129
|
+
while ((*t != *s) && (*t != '\0'))
|
130
|
+
t++;
|
131
|
+
value = _make_string(ctx, (char *)s+1, (t-s-1));
|
132
|
+
if (*t == *s) {
|
133
|
+
s = t + 1;
|
134
|
+
} else {
|
135
|
+
s = t;
|
136
|
+
}
|
137
|
+
/* otherwise, accumulate a value ending in ',' or '\0' */
|
138
|
+
} else {
|
139
|
+
while ((*t != ',') && (*t != '\0')) t++;
|
140
|
+
value = _make_string(ctx, (char *)s, (t-s));
|
141
|
+
s = t;
|
142
|
+
}
|
143
|
+
if (value == NULL) {
|
144
|
+
xmpp_free(ctx, key);
|
145
|
+
break;
|
146
|
+
}
|
147
|
+
/* TODO: check for collisions per spec */
|
148
|
+
hash_add(result, key, value);
|
149
|
+
/* hash table now owns the value, free the key */
|
150
|
+
xmpp_free(ctx, key);
|
151
|
+
}
|
152
|
+
}
|
153
|
+
xmpp_free(ctx, text);
|
154
|
+
|
155
|
+
return result;
|
156
|
+
}
|
157
|
+
|
158
|
+
/** expand a 16 byte MD5 digest to a 32 byte hex representation */
|
159
|
+
static void _digest_to_hex(const char *digest, char *hex)
|
160
|
+
{
|
161
|
+
int i;
|
162
|
+
const char hexdigit[] = "0123456789abcdef";
|
163
|
+
|
164
|
+
for (i = 0; i < 16; i++) {
|
165
|
+
*hex++ = hexdigit[ (digest[i] >> 4) & 0x0F ];
|
166
|
+
*hex++ = hexdigit[ digest[i] & 0x0F ];
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
/** append 'key="value"' to a buffer, growing as necessary */
|
171
|
+
static char *_add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key,
|
172
|
+
char *buf, int *len, int quote)
|
173
|
+
{
|
174
|
+
int olen,nlen;
|
175
|
+
int keylen, valuelen;
|
176
|
+
const char *value, *qvalue;
|
177
|
+
char *c;
|
178
|
+
|
179
|
+
/* allocate a zero-length string if necessary */
|
180
|
+
if (buf == NULL) {
|
181
|
+
buf = xmpp_alloc(ctx, 1);
|
182
|
+
buf[0] = '\0';
|
183
|
+
}
|
184
|
+
if (buf == NULL) return NULL;
|
185
|
+
|
186
|
+
/* get current string length */
|
187
|
+
olen = strlen(buf);
|
188
|
+
value = hash_get(table, key);
|
189
|
+
if (value == NULL) {
|
190
|
+
xmpp_error(ctx, "SASL", "couldn't retrieve value for '%s'", key);
|
191
|
+
value = "";
|
192
|
+
}
|
193
|
+
if (quote) {
|
194
|
+
qvalue = _make_quoted(ctx, value);
|
195
|
+
} else {
|
196
|
+
qvalue = value;
|
197
|
+
}
|
198
|
+
/* added length is key + '=' + value */
|
199
|
+
/* (+ ',' if we're not the first entry */
|
200
|
+
keylen = strlen(key);
|
201
|
+
valuelen = strlen(qvalue);
|
202
|
+
nlen = (olen ? 1 : 0) + keylen + 1 + valuelen + 1;
|
203
|
+
buf = xmpp_realloc(ctx, buf, olen+nlen);
|
204
|
+
|
205
|
+
if (buf != NULL) {
|
206
|
+
c = buf + olen;
|
207
|
+
if (olen) *c++ = ',';
|
208
|
+
memcpy(c, key, keylen); c += keylen;
|
209
|
+
*c++ = '=';
|
210
|
+
memcpy(c, qvalue, valuelen); c += valuelen;
|
211
|
+
*c++ = '\0';
|
212
|
+
}
|
213
|
+
|
214
|
+
if (quote) xmpp_free(ctx, (char *)qvalue);
|
215
|
+
|
216
|
+
return buf;
|
217
|
+
}
|
218
|
+
|
219
|
+
/** generate auth response string for the SASL DIGEST-MD5 mechanism */
|
220
|
+
char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
221
|
+
const char *jid, const char *password) {
|
222
|
+
hash_t *table;
|
223
|
+
char *result = NULL;
|
224
|
+
char *node, *domain, *realm;
|
225
|
+
char *value;
|
226
|
+
char *response;
|
227
|
+
int rlen;
|
228
|
+
struct MD5Context MD5;
|
229
|
+
unsigned char digest[16], HA1[16], HA2[16];
|
230
|
+
char hex[32];
|
231
|
+
|
232
|
+
/* our digest response is
|
233
|
+
Hex( KD( HEX(MD5(A1)),
|
234
|
+
nonce ':' nc ':' cnonce ':' qop ':' HEX(MD5(A2))
|
235
|
+
))
|
236
|
+
|
237
|
+
where KD(k, s) = MD5(k ':' s),
|
238
|
+
A1 = MD5( node ':' realm ':' password ) ':' nonce ':' cnonce
|
239
|
+
A2 = "AUTHENTICATE" ':' "xmpp/" domain
|
240
|
+
|
241
|
+
If there is an authzid it is ':'-appended to A1 */
|
242
|
+
|
243
|
+
/* parse the challenge */
|
244
|
+
table = _parse_digest_challenge(ctx, challenge);
|
245
|
+
if (table == NULL) {
|
246
|
+
xmpp_error(ctx, "SASL", "couldn't parse digest challenge");
|
247
|
+
return NULL;
|
248
|
+
}
|
249
|
+
|
250
|
+
node = xmpp_jid_node(ctx, jid);
|
251
|
+
domain = xmpp_jid_domain(ctx, jid);
|
252
|
+
|
253
|
+
/* generate default realm of domain if one didn't come from the
|
254
|
+
server */
|
255
|
+
realm = hash_get(table, "realm");
|
256
|
+
if (realm == NULL || strlen(realm) == 0) {
|
257
|
+
hash_add(table, "realm", xmpp_strdup(ctx, domain));
|
258
|
+
realm = hash_get(table, "realm");
|
259
|
+
}
|
260
|
+
|
261
|
+
/* add our response fields */
|
262
|
+
hash_add(table, "username", xmpp_strdup(ctx, node));
|
263
|
+
/* TODO: generate a random cnonce */
|
264
|
+
hash_add(table, "cnonce", xmpp_strdup(ctx, "00DEADBEEF00"));
|
265
|
+
hash_add(table, "nc", xmpp_strdup(ctx, "00000001"));
|
266
|
+
hash_add(table, "qop", xmpp_strdup(ctx, "auth"));
|
267
|
+
value = xmpp_alloc(ctx, 5 + strlen(domain) + 1);
|
268
|
+
memcpy(value, "xmpp/", 5);
|
269
|
+
memcpy(value+5, domain, strlen(domain));
|
270
|
+
value[5+strlen(domain)] = '\0';
|
271
|
+
hash_add(table, "digest-uri", value);
|
272
|
+
|
273
|
+
/* generate response */
|
274
|
+
|
275
|
+
/* construct MD5(node : realm : password) */
|
276
|
+
MD5Init(&MD5);
|
277
|
+
MD5Update(&MD5, (unsigned char *)node, strlen(node));
|
278
|
+
MD5Update(&MD5, (unsigned char *)":", 1);
|
279
|
+
MD5Update(&MD5, (unsigned char *)realm, strlen(realm));
|
280
|
+
MD5Update(&MD5, (unsigned char *)":", 1);
|
281
|
+
MD5Update(&MD5, (unsigned char *)password, strlen(password));
|
282
|
+
MD5Final(digest, &MD5);
|
283
|
+
|
284
|
+
/* digest now contains the first field of A1 */
|
285
|
+
|
286
|
+
MD5Init(&MD5);
|
287
|
+
MD5Update(&MD5, digest, 16);
|
288
|
+
MD5Update(&MD5, (unsigned char *)":", 1);
|
289
|
+
value = hash_get(table, "nonce");
|
290
|
+
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
291
|
+
MD5Update(&MD5, (unsigned char *)":", 1);
|
292
|
+
value = hash_get(table, "cnonce");
|
293
|
+
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
294
|
+
MD5Final(digest, &MD5);
|
295
|
+
|
296
|
+
/* now digest is MD5(A1) */
|
297
|
+
memcpy(HA1, digest, 16);
|
298
|
+
|
299
|
+
/* construct MD5(A2) */
|
300
|
+
MD5Init(&MD5);
|
301
|
+
MD5Update(&MD5, (unsigned char *)"AUTHENTICATE:", 13);
|
302
|
+
value = hash_get(table, "digest-uri");
|
303
|
+
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
304
|
+
if (strcmp(hash_get(table, "qop"), "auth") != 0) {
|
305
|
+
MD5Update(&MD5, (unsigned char *)":00000000000000000000000000000000",
|
306
|
+
33);
|
307
|
+
}
|
308
|
+
MD5Final(digest, &MD5);
|
309
|
+
|
310
|
+
memcpy(HA2, digest, 16);
|
311
|
+
|
312
|
+
/* construct response */
|
313
|
+
MD5Init(&MD5);
|
314
|
+
_digest_to_hex((char *)HA1, hex);
|
315
|
+
MD5Update(&MD5, (unsigned char *)hex, 32);
|
316
|
+
MD5Update(&MD5, (unsigned char *)":", 1);
|
317
|
+
value = hash_get(table, "nonce");
|
318
|
+
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
319
|
+
MD5Update(&MD5, (unsigned char *)":", 1);
|
320
|
+
value = hash_get(table, "nc");
|
321
|
+
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
322
|
+
MD5Update(&MD5, (unsigned char *)":", 1);
|
323
|
+
value = hash_get(table, "cnonce");
|
324
|
+
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
325
|
+
MD5Update(&MD5, (unsigned char *)":", 1);
|
326
|
+
value = hash_get(table, "qop");
|
327
|
+
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
328
|
+
MD5Update(&MD5, (unsigned char *)":", 1);
|
329
|
+
_digest_to_hex((char *)HA2, hex);
|
330
|
+
MD5Update(&MD5, (unsigned char *)hex, 32);
|
331
|
+
MD5Final(digest, &MD5);
|
332
|
+
|
333
|
+
response = xmpp_alloc(ctx, 32+1);
|
334
|
+
_digest_to_hex((char *)digest, hex);
|
335
|
+
memcpy(response, hex, 32);
|
336
|
+
response[32] = '\0';
|
337
|
+
hash_add(table, "response", response);
|
338
|
+
|
339
|
+
/* construct reply */
|
340
|
+
result = NULL;
|
341
|
+
rlen = 0;
|
342
|
+
result = _add_key(ctx, table, "username", result, &rlen, 1);
|
343
|
+
result = _add_key(ctx, table, "realm", result, &rlen, 1);
|
344
|
+
result = _add_key(ctx, table, "nonce", result, &rlen, 1);
|
345
|
+
result = _add_key(ctx, table, "cnonce", result, &rlen, 1);
|
346
|
+
result = _add_key(ctx, table, "nc", result, &rlen, 0);
|
347
|
+
result = _add_key(ctx, table, "qop", result, &rlen, 0);
|
348
|
+
result = _add_key(ctx, table, "digest-uri", result, &rlen, 1);
|
349
|
+
result = _add_key(ctx, table, "response", result, &rlen, 0);
|
350
|
+
result = _add_key(ctx, table, "charset", result, &rlen, 0);
|
351
|
+
|
352
|
+
xmpp_free(ctx, node);
|
353
|
+
xmpp_free(ctx, domain);
|
354
|
+
hash_release(table); /* also frees value strings */
|
355
|
+
|
356
|
+
/* reuse response for the base64 encode of our result */
|
357
|
+
response = base64_encode(ctx, (unsigned char *)result, strlen(result));
|
358
|
+
xmpp_free(ctx, result);
|
359
|
+
|
360
|
+
return response;
|
361
|
+
}
|
362
|
+
|
363
|
+
|
364
|
+
/** Base64 encoding routines. Implemented according to RFC 3548 */
|
365
|
+
|
366
|
+
/** map of all byte values to the base64 values, or to
|
367
|
+
'65' which indicates an invalid character. '=' is '64' */
|
368
|
+
static const char _base64_invcharmap[256] = {
|
369
|
+
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
370
|
+
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
371
|
+
65,65,65,65, 65,65,65,65, 65,65,65,62, 65,65,65,63,
|
372
|
+
52,53,54,55, 56,57,58,59, 60,61,65,65, 65,64,65,65,
|
373
|
+
65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
|
374
|
+
15,16,17,18, 19,20,21,22, 23,24,25,65, 65,65,65,65,
|
375
|
+
65,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
|
376
|
+
41,42,43,44, 45,46,47,48, 49,50,51,65, 65,65,65,65,
|
377
|
+
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
378
|
+
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
379
|
+
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
380
|
+
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
381
|
+
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
382
|
+
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
383
|
+
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
384
|
+
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65
|
385
|
+
};
|
386
|
+
|
387
|
+
/** map of all 6-bit values to their corresponding byte
|
388
|
+
in the base64 alphabet. Padding char is the value '64' */
|
389
|
+
static const char _base64_charmap[65] = {
|
390
|
+
'A','B','C','D', 'E','F','G','H',
|
391
|
+
'I','J','K','L', 'M','N','O','P',
|
392
|
+
'Q','R','S','T', 'U','V','W','X',
|
393
|
+
'Y','Z','a','b', 'c','d','e','f',
|
394
|
+
'g','h','i','j', 'k','l','m','n',
|
395
|
+
'o','p','q','r', 's','t','u','v',
|
396
|
+
'w','x','y','z', '0','1','2','3',
|
397
|
+
'4','5','6','7', '8','9','+','/',
|
398
|
+
'='
|
399
|
+
};
|
400
|
+
|
401
|
+
int base64_encoded_len(xmpp_ctx_t *ctx, const unsigned len)
|
402
|
+
{
|
403
|
+
/* encoded steam is 4 bytes for every three, rounded up */
|
404
|
+
return ((len + 2)/3) << 2;
|
405
|
+
}
|
406
|
+
|
407
|
+
char *base64_encode(xmpp_ctx_t *ctx,
|
408
|
+
const unsigned char * const buffer, const unsigned len)
|
409
|
+
{
|
410
|
+
int clen;
|
411
|
+
char *cbuf, *c;
|
412
|
+
uint32_t word, hextet;
|
413
|
+
int i;
|
414
|
+
|
415
|
+
clen = base64_encoded_len(ctx, len);
|
416
|
+
cbuf = xmpp_alloc(ctx, clen + 1);
|
417
|
+
if (cbuf != NULL) {
|
418
|
+
|
419
|
+
int inlen = len;
|
420
|
+
int outlen = clen + 1;
|
421
|
+
const unsigned char* in = buffer;
|
422
|
+
char* out = cbuf;
|
423
|
+
static const char b64str[64] =
|
424
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
425
|
+
|
426
|
+
while (inlen && outlen)
|
427
|
+
{
|
428
|
+
*out++ = b64str[(in[0] >> 2) & 0x3f];
|
429
|
+
if (!--outlen)
|
430
|
+
break;
|
431
|
+
*out++ = b64str[((in[0] << 4)
|
432
|
+
+ (--inlen ? in[1] >> 4 : 0))
|
433
|
+
& 0x3f];
|
434
|
+
if (!--outlen)
|
435
|
+
break;
|
436
|
+
*out++ =
|
437
|
+
(inlen
|
438
|
+
? b64str[((in[1] << 2)
|
439
|
+
+ (--inlen ? in[2] >> 6 : 0))
|
440
|
+
& 0x3f]
|
441
|
+
: '=');
|
442
|
+
if (!--outlen)
|
443
|
+
break;
|
444
|
+
*out++ = inlen ? b64str[in[2] & 0x3f] : '=';
|
445
|
+
if (!--outlen)
|
446
|
+
break;
|
447
|
+
if (inlen)
|
448
|
+
inlen--;
|
449
|
+
if (inlen)
|
450
|
+
in += 3;
|
451
|
+
}
|
452
|
+
|
453
|
+
if (outlen)
|
454
|
+
*out = '\0';
|
455
|
+
}
|
456
|
+
|
457
|
+
return cbuf;
|
458
|
+
}
|
459
|
+
|
460
|
+
int base64_decoded_len(xmpp_ctx_t *ctx,
|
461
|
+
const char * const buffer, const unsigned len)
|
462
|
+
{
|
463
|
+
int nudge;
|
464
|
+
int c;
|
465
|
+
|
466
|
+
/* count the padding characters for the remainder */
|
467
|
+
nudge = -1;
|
468
|
+
c = _base64_invcharmap[(int)buffer[len-1]];
|
469
|
+
if (c < 64) nudge = 0;
|
470
|
+
else if (c == 64) {
|
471
|
+
c = _base64_invcharmap[(int)buffer[len-2]];
|
472
|
+
if (c < 64) nudge = 1;
|
473
|
+
else if (c == 64) {
|
474
|
+
c = _base64_invcharmap[(int)buffer[len-3]];
|
475
|
+
if (c < 64) nudge = 2;
|
476
|
+
}
|
477
|
+
}
|
478
|
+
if (nudge < 0) return 0; /* reject bad coding */
|
479
|
+
|
480
|
+
/* decoded steam is 3 bytes for every four */
|
481
|
+
return 3 * (len >> 2) - nudge;
|
482
|
+
}
|
483
|
+
|
484
|
+
unsigned char *base64_decode(xmpp_ctx_t *ctx,
|
485
|
+
const char * const buffer, const unsigned len)
|
486
|
+
{
|
487
|
+
int dlen;
|
488
|
+
unsigned char *dbuf, *d;
|
489
|
+
uint32_t word, hextet;
|
490
|
+
int i;
|
491
|
+
|
492
|
+
/* len must be a multiple of 4 */
|
493
|
+
if (len & 0x03) return NULL;
|
494
|
+
|
495
|
+
dlen = base64_decoded_len(ctx, buffer, len);
|
496
|
+
dbuf = xmpp_alloc(ctx, dlen + 1);
|
497
|
+
if (dbuf != NULL) {
|
498
|
+
d = dbuf;
|
499
|
+
/* loop over each set of 4 characters, decoding 3 bytes */
|
500
|
+
for (i = 0; i < len - 3; i += 4) {
|
501
|
+
hextet = _base64_invcharmap[(int)buffer[i]];
|
502
|
+
if (hextet & 0xC0) break;
|
503
|
+
word = hextet << 18;
|
504
|
+
hextet = _base64_invcharmap[(int)buffer[i+1]];
|
505
|
+
if (hextet & 0xC0) break;
|
506
|
+
word |= hextet << 12;
|
507
|
+
hextet = _base64_invcharmap[(int)buffer[i+2]];
|
508
|
+
if (hextet & 0xC0) break;
|
509
|
+
word |= hextet << 6;
|
510
|
+
hextet = _base64_invcharmap[(int)buffer[i+3]];
|
511
|
+
if (hextet & 0xC0) break;
|
512
|
+
word |= hextet;
|
513
|
+
*d++ = (word & 0x00FF0000) >> 16;
|
514
|
+
*d++ = (word & 0x0000FF00) >> 8;
|
515
|
+
*d++ = (word & 0x000000FF);
|
516
|
+
}
|
517
|
+
if (hextet > 64) goto _base64_decode_error;
|
518
|
+
/* handle the remainder */
|
519
|
+
switch (dlen % 3) {
|
520
|
+
case 0:
|
521
|
+
/* nothing to do */
|
522
|
+
break;
|
523
|
+
case 1:
|
524
|
+
/* redo the last quartet, checking for correctness */
|
525
|
+
hextet = _base64_invcharmap[(int)buffer[len-4]];
|
526
|
+
if (hextet & 0xC0) goto _base64_decode_error;
|
527
|
+
word = hextet << 2;
|
528
|
+
hextet = _base64_invcharmap[(int)buffer[len-3]];
|
529
|
+
if (hextet & 0xC0) goto _base64_decode_error;
|
530
|
+
word |= hextet >> 4;
|
531
|
+
*d++ = word & 0xFF;
|
532
|
+
hextet = _base64_invcharmap[(int)buffer[len-2]];
|
533
|
+
if (hextet != 64) goto _base64_decode_error;
|
534
|
+
hextet = _base64_invcharmap[(int)buffer[len-1]];
|
535
|
+
if (hextet != 64) goto _base64_decode_error;
|
536
|
+
break;
|
537
|
+
case 2:
|
538
|
+
/* redo the last quartet, checking for correctness */
|
539
|
+
hextet = _base64_invcharmap[(int)buffer[len-4]];
|
540
|
+
if (hextet & 0xC0) goto _base64_decode_error;
|
541
|
+
word = hextet << 10;
|
542
|
+
hextet = _base64_invcharmap[(int)buffer[len-3]];
|
543
|
+
if (hextet & 0xC0) goto _base64_decode_error;
|
544
|
+
word |= hextet << 4;
|
545
|
+
hextet = _base64_invcharmap[(int)buffer[len-2]];
|
546
|
+
if (hextet & 0xC0) goto _base64_decode_error;
|
547
|
+
word |= hextet >> 2;
|
548
|
+
*d++ = (word & 0xFF00) >> 8;
|
549
|
+
*d++ = (word & 0x00FF);
|
550
|
+
hextet = _base64_invcharmap[(int)buffer[len-1]];
|
551
|
+
if (hextet != 64) goto _base64_decode_error;
|
552
|
+
break;
|
553
|
+
}
|
554
|
+
}
|
555
|
+
*d = '\0';
|
556
|
+
return dbuf;
|
557
|
+
|
558
|
+
_base64_decode_error:
|
559
|
+
/* invalid character; abort decoding! */
|
560
|
+
xmpp_free(ctx, dbuf);
|
561
|
+
return NULL;
|
562
|
+
}
|
563
|
+
|
564
|
+
/*** self tests ***/
|
565
|
+
#ifdef TEST
|
566
|
+
|
567
|
+
#include <stdio.h>
|
568
|
+
|
569
|
+
int test_charmap_identity(void)
|
570
|
+
{
|
571
|
+
int i, v, u;
|
572
|
+
|
573
|
+
for (i = 0; i < 65; i++) {
|
574
|
+
v = _base64_charmap[i];
|
575
|
+
if (v > 255) return 1;
|
576
|
+
u = _base64_invcharmap[v];
|
577
|
+
/* printf("map: %d -> %d -> %d\n", i, v, u); */
|
578
|
+
if (u != i) return 1;
|
579
|
+
}
|
580
|
+
|
581
|
+
return 0;
|
582
|
+
}
|
583
|
+
|
584
|
+
int test_charmap_range(void)
|
585
|
+
{
|
586
|
+
int i, v;
|
587
|
+
|
588
|
+
for (i = 64; i < 256; i++) {
|
589
|
+
v = _base64_invcharmap[i];
|
590
|
+
if (i < 64) return 1;
|
591
|
+
}
|
592
|
+
|
593
|
+
return 0;
|
594
|
+
}
|
595
|
+
|
596
|
+
int main(int argc, char *argv[])
|
597
|
+
{
|
598
|
+
int ret = 0;
|
599
|
+
|
600
|
+
printf("testing charmap identity...");
|
601
|
+
ret = test_charmap_identity();
|
602
|
+
if (ret) return ret;
|
603
|
+
printf(" ok.\n");
|
604
|
+
|
605
|
+
printf("testing charmap range...");
|
606
|
+
ret = test_charmap_range();
|
607
|
+
if (ret) return ret;
|
608
|
+
printf(" ok.\n");
|
609
|
+
|
610
|
+
printf("no error\n");
|
611
|
+
return 0;
|
612
|
+
}
|
613
|
+
|
614
|
+
#endif /* TEST */
|
data/ext/sasl.h
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
/* sasl.h
|
2
|
+
** strophe XMPP client library -- SASL authentication helpers
|
3
|
+
**
|
4
|
+
** Copyright (C) 2005-2008 OGG, LLC. All rights reserved.
|
5
|
+
**
|
6
|
+
** This software is provided AS-IS with no warranty, either express
|
7
|
+
** or implied.
|
8
|
+
**
|
9
|
+
** This software is distributed under license and may not be copied,
|
10
|
+
** modified or distributed except as expressly authorized under the
|
11
|
+
** terms of the license contained in the file LICENSE.txt in this
|
12
|
+
** distribution.
|
13
|
+
*/
|
14
|
+
|
15
|
+
/** @file
|
16
|
+
* SASL authentication helpers.
|
17
|
+
*/
|
18
|
+
|
19
|
+
#ifndef __LIBSTROPHE_SASL_H__
|
20
|
+
#define __LIBSTROPHE_SASL_H__
|
21
|
+
|
22
|
+
#include "strophe.h"
|
23
|
+
|
24
|
+
/** low-level sasl routines */
|
25
|
+
|
26
|
+
char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password);
|
27
|
+
char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
28
|
+
const char *jid, const char *password);
|
29
|
+
|
30
|
+
|
31
|
+
/** Base64 encoding routines. Implemented according to RFC 3548 */
|
32
|
+
|
33
|
+
int base64_encoded_len(xmpp_ctx_t *ctx, const unsigned len);
|
34
|
+
|
35
|
+
char *base64_encode(xmpp_ctx_t *ctx,
|
36
|
+
const unsigned char * const buffer, const unsigned len);
|
37
|
+
|
38
|
+
int base64_decoded_len(xmpp_ctx_t *ctx,
|
39
|
+
const char * const buffer, const unsigned len);
|
40
|
+
|
41
|
+
unsigned char *base64_decode(xmpp_ctx_t *ctx,
|
42
|
+
const char * const buffer, const unsigned len);
|
43
|
+
|
44
|
+
#endif /* _LIBXMPP_SASL_H__ */
|