libsyncml  0.5.4
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 
SmlBool smlSessionSendReply(SmlSession *session, SmlStatus *status, SmlError **error)
Sends a reply to a command.
Definition: sml_session.c:2536
const char * smlErrorPrint(SmlError **error)
Returns the message of the error.
Definition: sml_error.c:299
void smlTrace(SmlTraceType type, const char *message,...)
Used for tracing the application.
Definition: sml_support.c:120
void * smlTryMalloc0(long n_bytes, SmlError **error)
Safely mallocs.
Definition: sml_support.c:335
void smlErrorSet(SmlError **error, SmlErrorType type, const char *format,...)
Sets the error.
Definition: sml_error.c:355
Represent an error.