libsyncml 0.5.4

- Main Page
- Modules
- Data Structures
- Files

- File List

sml_auth.c
1 /*
2 * libsyncml - A syncml protocol implementation
3 * Copyright (C) 2005 Armin Bauer <armin.bauer@opensync.org>
4 * Copyright (C) 2008 Michael Bell <michael.bell@opensync.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22 #include <libsyncml/syncml.h>
23 #include "sml_auth.h"
24
25 #include <libsyncml/syncml_internals.h>
26 #include "sml_auth_internals.h"
27 #include <libsyncml/sml_session_internals.h>
28 #include <libsyncml/sml_elements_internals.h>
29 #include <libsyncml/sml_command_internals.h>
30 #include "libsyncml/sml_error_internals.h"
31
32 static SmlStatus *_smlAuthHeaderReply(
33 SmlSession *session,
34 SmlErrorType code,
35 SmlAuthType auth,
36 SmlError **error);
37
38 void _status_callback(SmlSession *session, SmlStatus *status, void *userdata)
39 {
40 smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, userdata);
41
42 smlTrace(TRACE_EXIT, "%s", __func__);
43 }
44
45 void _header_callback(SmlSession *session, SmlHeader *header, SmlCred *cred, void *userdata)
46 {
47 smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, session, header, cred, userdata);
48 smlAssert(session);
49 smlAssert(userdata);
50 SmlStatus *reply = NULL;
51 SmlAuthenticator *auth = userdata;
52 SmlError *error = NULL;
53
54 if (session->sessionType == SML_SESSION_TYPE_CLIENT) {
55 /* If this is an OMA DS client then there will be no
56 * authentication. Only OMA DS servers can request an
57 * authentication from the remote peer.
58 */
59 g_warning("This is an OMA DS client. An OMA DS client should not use this authentication callback.");
60 smlTrace(TRACE_INTERNAL, "%s: This is an OMA DS client and so auth is not supported.", __func__);
61 auth->state = SML_NO_ERROR;
62 if (auth->enabled)
63 smlTrace(TRACE_ERROR,
64 "%s: authentication is enabled but this is an OMA DS client.");
65 }
66 else if (!cred)
67 {
68 if (!auth->enabled)
69 {
70 smlTrace(TRACE_INTERNAL, "%s: Auth is disabled and no cred given", __func__);
71 auth->state = SML_NO_ERROR;
72 } else {
73 /* auth->enabled */
74 if (auth->state != SML_AUTH_ACCEPTED)
75 {
76 /* Ask the remote peer to authenticate.
77 * This is not an error because it is really dangerous
78 * for the remote peer to send its password without
79 * be asked for it. Please note that syncml:auth-basic
80 * is a clear text password.
81 */
82 smlTrace(TRACE_INTERNAL, "%s: Auth is required", __func__);
83 auth->state = SML_ERROR_AUTH_REQUIRED;
84 session->authenticate = TRUE;
85 }
86 else
87 {
88 /* auth->state == SML_AUTH_ACCEPTED */
89 smlTrace(TRACE_INTERNAL, "%s: Auth is already accepted.", __func__);
90 auth->state = SML_AUTH_ACCEPTED;
91 }
92 }
93 } else {
94 /* cred available */
95 smlTrace(TRACE_INTERNAL, "%s: Cred is \"%s\"", __func__, VA_STRING(cred->data));
96 if (!auth->enabled)
97 {
98 smlTrace(TRACE_INTERNAL, "%s: Cred received but unwanted", __func__);
99 auth->state = SML_AUTH_ACCEPTED;
100 } else {
101 /* auth->enabled */
102 if (auth->verifyCallback)
103 {
104 /* The callback needs the following stuff:
105 * Chal
106 * Cred
107 * LocName(username)
108 */
109 if (auth->verifyCallback(session->chal, cred,
110 smlLocationGetName(session->source),
111 auth->verifyCallbackUserdata, &error))
112 {
113 auth->state = SML_AUTH_ACCEPTED;
114 } else {
115 smlErrorSet(&error, SML_ERROR_AUTH_REJECTED,
116 "Auth rejected for username %s",
117 smlLocationGetName(session->source));
118 smlSessionDispatchEvent(session, SML_SESSION_EVENT_ERROR, NULL, NULL, NULL, error);
119 smlErrorDeref(&error);
120 auth->state = SML_ERROR_AUTH_REJECTED;
121 }
122 } else {
123 smlTrace(TRACE_INTERNAL, "%s: No verify callback set", __func__);
124 auth->state = SML_ERROR_AUTH_REJECTED;
125 }
126 }
127 }
128
129 if (auth->state == SML_ERROR_AUTH_REJECTED) {
130 smlTrace(TRACE_INTERNAL, "%s: Ending session due to wrong / missing creds", __func__);
131 session->end = TRUE;
132 }
133
134 reply = _smlAuthHeaderReply(session, auth->state, auth->type, &error);
135 if (!reply)
136 goto error;
137
138 if (!smlSessionSendReply(session, reply, &error)) {
139 smlStatusUnref(reply);
140 goto error;
141 }
142
143 smlStatusUnref(reply);
144
145 if (!session->established && !session->end &&
146 !session->authenticate &&
147 session->sessionType == SML_SESSION_TYPE_SERVER)
148 {
149 session->established = TRUE;
150 smlSessionDispatchEvent(
151 session, SML_SESSION_EVENT_ESTABLISHED,
152 NULL, NULL, NULL, NULL);
153 }
154
155 smlTrace(TRACE_EXIT, "%s", __func__);
156 return;
157 error:
158 smlSessionDispatchEvent(session, SML_SESSION_EVENT_ERROR, NULL, NULL, NULL, error);
159 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
160 smlErrorDeref(&error);
161 return;
162 }
163
164 char *smlAuthGetCredString(
165 SmlAuthType type,
166 const char *username,
167 const char *password,
168 const char *b64_nonce,
169 SmlError **error)
170 {
171 smlTrace(TRACE_ENTRY, "%s", __func__);
172 CHECK_ERROR_REF
173 char *cred = NULL;
174
175 switch (type) {
176 case SML_AUTH_TYPE_BASIC:
177
178 smlTrace(TRACE_INTERNAL, "%s - SML_AUTH_TYPE_BASIC", __func__);
179 char *plain = g_strjoin(":", username, password, NULL);
180 cred = g_base64_encode((unsigned char *) plain, strlen(plain));
181 if (!cred) {
182 smlErrorSet(error, SML_ERROR_GENERIC,
183 "The syncml:auth-basic credential cannot be base64 encoded.");
184 smlSafeCFree(&plain);
185 goto error;
186 }
187 smlSafeCFree(&plain);
188
189 break;
190 case SML_AUTH_TYPE_MD5:
191
192 smlTrace(TRACE_INTERNAL, "%s - SML_AUTH_TYPE_MD5", __func__);
193 /* How does syncml:auth-md5 works?
194 *
195 * base64(
196 * md5(
197 * base64(
198 * md5(
199 * username + ":" + password
200 * )
201 * ) +
202 * ":" + nonce
203 * )
204 * )
205 */
206
207 /* Let's determine the string for the comparison. */
208 ;
209 char *auth = g_strjoin (":", username, password, NULL);
210 unsigned char digest[16];
211 smlMD5GetDigest (auth, strlen(auth), digest);
212 smlSafeCFree(&auth);
213 cred = g_base64_encode(digest, 16);
214 if (!cred) {
215 smlErrorSet(error, SML_ERROR_GENERIC,
216 "The username:password part of the syncml:auth-md5 "\
217 "credential cannot be base64 encoded.");
218 goto error;
219 }
220 auth = g_strjoin (":", cred, b64_nonce, NULL);
221 smlSafeCFree(&cred);
222 smlMD5GetDigest (auth, strlen(auth), digest);
223 smlSafeCFree(&auth);
224 cred = g_base64_encode(digest, 16);
225 if (!cred) {
226 smlErrorSet(error, SML_ERROR_GENERIC,
227 "The complete syncml:auth-md5 credential cannot be base64 encoded.");
228 goto error;
229 }
230
231 break;
232 default:
233 smlTrace(TRACE_ERROR, "%s - unknown authentication type", __func__);
234 smlErrorSet(error, SML_ERROR_GENERIC, "Unknown auth format");
235 goto error;
236 }
237
238 smlTrace(TRACE_EXIT, "%s", __func__);
239 return cred;
240 error:
241 smlTrace(TRACE_EXIT_ERROR, "%s - cannot create credential string");
242 if (*error == NULL)
243 smlErrorSet(error, SML_ERROR_GENERIC, "Cannot create credential string for user %s.",
244 username);
245 return NULL;
246 }
247
248 SmlBool smlAuthVerify(SmlChal *chal, SmlCred *cred, const char *username, const char *password, SmlError **error)
249 {
250 smlTrace(TRACE_ENTRY, "%s", __func__);
251 CHECK_ERROR_REF
252
253 /* If no Chal is send to the client but the client offers a Cred
254 * then we accept it. Theoretically this is not 100% perfect if
255 * syncml:auth-md5 is used but if the client offers it then we
256 * accept it because some implementations like UIQ 3 are broken
257 * and implemented the protocol in a wrong way if we answer with
258 * error 407 and a fresh nonce.
259 *
260 * SECURITY NOTE: This part of the code makes libsyncml vulnerable
261 * SECURITY NOTE: against replay attacks.
262 *
263 */
264 if (chal && chal->type != cred->type)
265 {
266 if (chal->type == SML_AUTH_TYPE_BASIC &&
267 cred->type == SML_AUTH_TYPE_MD5)
268 {
269 /* This is an upgrade to more security.
270 * So it is acceptable.
271 */
272 smlTrace(TRACE_INTERNAL, "%s - replace syncml:auth-basic by syncml:auth-md5", __func__);
273 } else {
274 /* This is a security event. */
275 smlErrorSet(error, SML_ERROR_AUTH_REJECTED,
276 "The type of the authentication was changed to a lower security level.");
277 goto error;
278 }
279 }
280 smlTrace(TRACE_INTERNAL, "%s - authentication security policy ok", __func__);
281
282 char *wanted = NULL;
283 switch (cred->type) {
284 case SML_AUTH_TYPE_BASIC:
285 smlTrace(TRACE_INTERNAL, "%s - SML_AUTH_TYPE_BASIC", __func__);
286 wanted = smlAuthGetCredString(SML_AUTH_TYPE_BASIC, username, password, NULL, error);
287 break;
288 case SML_AUTH_TYPE_MD5:
289 smlTrace(TRACE_INTERNAL, "%s - SML_AUTH_TYPE_MD5", __func__);
290 if (chal)
291 wanted = smlAuthGetCredString(
292 SML_AUTH_TYPE_MD5,
293 username, password,
294 chal->nonce_b64, error);
295 else
296 wanted = smlAuthGetCredString(
297 SML_AUTH_TYPE_MD5,
298 username, password,
299 "", error);
300 break;
301 default:
302 smlTrace(TRACE_ERROR, "%s - unknown authentication type", __func__);
303 smlErrorSet(error, SML_ERROR_GENERIC, "Unknown auth format");
304 goto error;
305 }
306 smlTrace(TRACE_INTERNAL, "%s - credential string calculated", __func__);
307
308 /* compare the authentication string */
309 if (strcmp(wanted, cred->data))
310 {
311 smlTrace(TRACE_INTERNAL, "%s - credentials mismatch", __func__);
312 smlSafeCFree(&wanted);
313 goto error;
314 }
315 smlSafeCFree(&wanted);
316
317 smlTrace(TRACE_EXIT, "%s", __func__);
318 return TRUE;
319 error:
320 smlTrace(TRACE_EXIT_ERROR, "%s - auth rejected");
321 if (*error == NULL)
322 smlErrorSet(error, SML_ERROR_AUTH_REJECTED, "Authentication rejected for username %s.",
323 username);
324 return FALSE;
325 }
326
327 SmlAuthenticator *smlAuthNew(SmlError **error)
328 {
329 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, error);
330 CHECK_ERROR_REF
331 SmlAuthenticator *auth = smlTryMalloc0(sizeof(SmlAuthenticator), error);
332 if (!auth)
333 goto error;
334
335 auth->enabled = TRUE;
336 auth->state = SML_ERROR_AUTH_REQUIRED;
337
338 smlTrace(TRACE_EXIT, "%s: %p", __func__, auth);
339 return auth;
340
341 error:
342 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
343 return NULL;
344 }
345
346
347 void smlAuthFree(SmlAuthenticator *auth)
348 {
349 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, auth);
350 smlAssert(auth);
351
352 smlSafeFree((gpointer *)&auth);
353
354 smlTrace(TRACE_EXIT, "%s", __func__);
355 }
356
357 SmlBool smlAuthRegister(SmlAuthenticator *auth, SmlManager *manager, SmlError **error)
358 {
359 smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, auth, manager, error);
360 CHECK_ERROR_REF
361 smlAssert(auth);
362 smlAssert(manager);
363
364 smlManagerRegisterHeaderHandler(manager, _header_callback, _status_callback, auth);
365
366 smlTrace(TRACE_EXIT, "%s", __func__);
367 return TRUE;
368 }
369
370 void smlAuthSetState(SmlAuthenticator *auth, SmlErrorType type)
371 {
372 smlTrace(TRACE_ENTRY, "%s(%p, %i)", __func__, auth, type);
373 smlAssert(auth);
374
375 auth->state = type;
376
377 smlTrace(TRACE_EXIT, "%s", __func__);
378 }
379
380 /* FIXME: DEPRECATED*/
381 SmlStatus *smlAuthHeaderReply(
382 SmlSession *session,
383 SmlAuthType code,
384 SmlError **error) {
385 CHECK_ERROR_REF
386 g_warning("SmlAuthType of smlAuthHeaderReply is used as SmlErrorType.");
387 return _smlAuthHeaderReply(session, (SmlErrorType) code, SML_AUTH_TYPE_BASIC, error);
388 }
389
390 static SmlStatus *_smlAuthHeaderReply(
391 SmlSession *session,
392 SmlErrorType code,
393 SmlAuthType auth,
394 SmlError **error)
395 {
396 smlTrace(TRACE_ENTRY, "%s(%p, %i, %i, %p)", __func__, session, code, auth, error);
397 CHECK_ERROR_REF
398
399 // the session structure is from the viewpoint of this machine
400 // SourceRef and TargetRef of the status MUST use the viewpoint
401 // of the remote peer
402 // we have to revert source and target
403 smlTrace(TRACE_INTERNAL, "%s: SourceRef: %s --> TargetRef: %s",
404 __func__, VA_STRING(session->target->locURI), VA_STRING(session->source->locURI));
405 SmlStatus *reply = smlStatusNew(code, 0, session->lastReceivedMessageID, session->target, session->source, SML_COMMAND_TYPE_HEADER, error);
406 if (!reply)
407 goto error;
408
409 if (code == SML_ERROR_AUTH_REJECTED ||
410 code == SML_ERROR_AUTH_REQUIRED) {
411 reply->chal = smlChalNew(auth, error);
412 if (!reply->chal)
413 goto error_free_reply;
414 session->chal = reply->chal;
415 smlChalRef(session->chal);
416 }
417
418 smlTrace(TRACE_EXIT, "%s: %p", __func__, reply);
419 return reply;
420
421 error_free_reply:
422 smlStatusUnref(reply);
423 error:
424 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
425 return NULL;
426 }
427
428 void smlAuthSetVerifyCallback(SmlAuthenticator *auth, SmlAuthVerifyCb callback, void *userdata)
429 {
430 smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, auth, callback, userdata);
431 smlAssert(auth);
432 auth->verifyCallback = callback;
433 auth->verifyCallbackUserdata = userdata;
434 smlTrace(TRACE_EXIT, "%s", __func__);
435 }
436
437 void smlAuthSetEnable(SmlAuthenticator *auth, SmlBool enabled)
438 {
439 smlTrace(TRACE_ENTRY, "%s(%p, %i)", __func__, auth, enabled);
440 smlAssert(auth);
441
442 auth->enabled = enabled;
443
444 smlTrace(TRACE_EXIT, "%s", __func__);
445 }
446
447 SmlBool smlAuthIsEnabled(SmlAuthenticator *auth)
448 {
449 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, auth);
450 smlAssert(auth);
451
452 smlTrace(TRACE_EXIT, "%s - %u", __func__, auth->enabled);
453 return auth->enabled;
454 }
455
456 void smlAuthSetType(SmlAuthenticator *auth, SmlAuthType type)
457 {
458 smlTrace(TRACE_ENTRY, "%s(%p, %i)", __func__, auth, type);
459 smlAssert(auth);
460 smlAssert(type != SML_AUTH_TYPE_UNKNOWN);
461
462 auth->type = type;
463
464 smlTrace(TRACE_EXIT, "%s", __func__);
465 }
466