stropheruby 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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__ */
|