1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
4 * This file is part of gsignond
6 * Copyright (C) 2013 Intel Corporation.
8 * Contact: Imran Zaman <imran.zaman@intel.com>
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
29 #include <gsignond/gsignond-plugin-interface.h>
30 #include "gsignond-digest-plugin.h"
31 #include <gsignond/gsignond-error.h>
32 #include <gsignond/gsignond-log.h>
33 #include <gsignond/gsignond-utils.h>
36 * SECTION:gsignond-digest-plugin
37 * @short_description: a plugin that performs HTTP Digest authentication
38 * @include: gsignond/gsignond-digest-plugin.h
40 * #GSignondDigestPlugin performs HTTP Digest authentication without exposing
41 * the password to the application. Digest authentication is described in
42 * <ulink url="http://tools.ietf.org/html/rfc2617">RFC 2617</ulink>.
44 * gsignond_plugin_request_initial() @session_data parameter should include
45 * the following string items, whose meaning is described in the RFC:
46 * - username and secret. If they are absent, they are requested from the user
48 * - realm, "Algo", "Nonce", "Method", "DigestUri" - mandatory items.
49 * - "NonceCount", "Qop", "HEntity". "NonceCount" must be present if "Qop" is
50 * present, "HEntity" must be present if "Qop" is present and set to "auth-int".
52 * If the plugin has all the data to calculate the digest, it issues
53 * #GSignondPlugin::response-final signal. @session_data in that signal contains
54 * the username, "CNonce" item and the digest value under the "Response" key.
56 * If some of the data is incorrect or not available, #GSignondPlugin::error
57 * signal is issued instead.
59 * #GSignondPlugin:type property is set to "digest", and #GSignondPlugin:mechanisms
60 * property contains a single entry "digest".
63 * GSignondDigestPlugin:
65 * Opaque #GSignondDigestPlugin data structure.
68 * GSignondDigestPluginClass:
69 * @parent_class: the parent class structure
71 * Opaque #GSignondDigestPluginClass data structure.
75 static void gsignond_plugin_interface_init (GSignondPluginInterface *iface);
77 G_DEFINE_TYPE_WITH_CODE (GSignondDigestPlugin, gsignond_digest_plugin,
79 G_IMPLEMENT_INTERFACE (GSIGNOND_TYPE_PLUGIN,
80 gsignond_plugin_interface_init));
82 #define GSIGNOND_DIGEST_PLUGIN_GET_PRIVATE(obj) \
83 (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\
84 GSIGNOND_TYPE_DIGEST_PLUGIN, \
85 GSignondDigestPluginPrivate))
87 #define DATA_SET_VALUE(data, key, value) \
89 gsignond_dictionary_set_string(data, key, value); \
91 #define TO_GUCHAR(data) ((const guchar*)data)
93 struct _GSignondDigestPluginPrivate
95 GSignondSessionData *session_data;
99 _gsignond_digest_plugin_compute_md5_digest (
101 const gchar* username,
105 const gchar* nonce_count,
109 const gchar* digest_uri,
110 const gchar* hentity)
112 GChecksum *a1 = NULL, *a2 = NULL, *response = NULL;
113 const gchar *ha1 = NULL, *ha2 = NULL;
114 gchar *hresponse = NULL;
116 a1 = g_checksum_new (G_CHECKSUM_MD5);
117 g_checksum_update (a1, TO_GUCHAR(username), strlen(username));
118 g_checksum_update (a1, TO_GUCHAR(":"), 1);
119 g_checksum_update (a1, TO_GUCHAR(realm), strlen(realm));
120 g_checksum_update (a1, TO_GUCHAR(":"), 1);
121 g_checksum_update (a1, TO_GUCHAR(secret), strlen(secret));
123 if (g_strcmp0 (algo, "md5-sess") == 0) {
124 GChecksum *a1_sess = NULL;
125 a1_sess = g_checksum_new (G_CHECKSUM_MD5);
126 ha1 = g_checksum_get_string (a1);
127 g_checksum_update (a1_sess, TO_GUCHAR(ha1), strlen(ha1));
128 g_checksum_update (a1_sess, TO_GUCHAR(":"), 1);
129 g_checksum_update (a1_sess, TO_GUCHAR(nonce), strlen(nonce));
130 g_checksum_update (a1_sess, TO_GUCHAR(":"), 1);
131 g_checksum_update (a1_sess, TO_GUCHAR(cnonce), strlen(cnonce));
132 g_checksum_free (a1);
136 a2 = g_checksum_new (G_CHECKSUM_MD5);
137 g_checksum_update (a2, TO_GUCHAR(method), strlen(method));
138 g_checksum_update (a2, TO_GUCHAR(":"), 1);
139 g_checksum_update (a2, TO_GUCHAR(digest_uri), strlen(digest_uri));
140 if (qop && g_strcmp0 (qop, "auth-int") == 0 && hentity) {
141 g_checksum_update (a2, TO_GUCHAR(":"), 1);
142 g_checksum_update (a2, TO_GUCHAR(hentity), strlen(hentity));
144 ha1 = g_checksum_get_string (a1);
145 ha2 = g_checksum_get_string (a2);
147 response = g_checksum_new (G_CHECKSUM_MD5);
148 g_checksum_update (response, TO_GUCHAR(ha1), strlen(ha1));
149 g_checksum_update (response, TO_GUCHAR(":"), 1);
150 g_checksum_update (response, TO_GUCHAR(nonce), strlen(nonce));
151 g_checksum_update (response, TO_GUCHAR(":"), 1);
153 g_checksum_update (response, TO_GUCHAR(nonce_count),
154 strlen(nonce_count));
155 g_checksum_update (response, TO_GUCHAR(":"), 1);
156 g_checksum_update (response, TO_GUCHAR(cnonce), strlen(cnonce));
157 g_checksum_update (response, TO_GUCHAR(":"), 1);
158 g_checksum_update (response, TO_GUCHAR(qop), strlen(qop));
159 g_checksum_update (response, TO_GUCHAR(":"), 1);
161 g_checksum_update (response, TO_GUCHAR(ha2), strlen(ha2));
162 hresponse = g_strdup(g_checksum_get_string (response));
163 g_checksum_free (response);
164 g_checksum_free (a2);
165 g_checksum_free (a1);
170 gsignond_digest_plugin_cancel (GSignondPlugin *self)
172 GError* error = g_error_new(GSIGNOND_ERROR,
173 GSIGNOND_ERROR_SESSION_CANCELED,
174 "Session cancelled");
175 gsignond_plugin_error (self, error);
180 gsignond_digest_plugin_request (
181 GSignondPlugin *self,
182 GSignondSessionData *session_data)
187 _gsignond_digest_plugin_return_digest(GSignondPlugin *plugin,
188 const gchar *username,
190 GSignondDictionary *session_data)
192 g_return_if_fail (plugin != NULL);
193 g_return_if_fail (GSIGNOND_IS_DIGEST_PLUGIN (plugin));
195 GSignondSessionData *response = NULL;
196 const gchar* realm = gsignond_session_data_get_realm (session_data);
197 const gchar* algo = gsignond_dictionary_get_string (session_data,
199 const gchar* nonce = gsignond_dictionary_get_string (session_data,
201 const gchar* nonce_count = gsignond_dictionary_get_string (session_data,
203 const gchar* qop = gsignond_dictionary_get_string (session_data,
205 const gchar* method = gsignond_dictionary_get_string (session_data,
207 const gchar* digest_uri = gsignond_dictionary_get_string (session_data,
209 const gchar* hentity = gsignond_dictionary_get_string (session_data,
211 gchar *cnonce = gsignond_generate_nonce ();
213 GError* error = g_error_new (GSIGNOND_ERROR,
214 GSIGNOND_ERROR_MISSING_DATA, "Error in generating nonce");
215 gsignond_plugin_error (plugin, error);
216 g_error_free (error);
220 if ((!realm || !algo || !nonce || !method || !digest_uri)
221 || (qop && g_strcmp0 (qop, "auth-int") == 0 && !hentity)
222 || (qop && !nonce_count)) {
223 GError* error = g_error_new (GSIGNOND_ERROR,
224 GSIGNOND_ERROR_MISSING_DATA, "Missing Session Data");
225 gsignond_plugin_error (plugin, error);
226 g_error_free (error);
229 gchar *digest = _gsignond_digest_plugin_compute_md5_digest(algo,
230 username,realm, secret, nonce, nonce_count, cnonce, qop, method,
231 digest_uri, hentity);
233 response = gsignond_dictionary_new();
234 gsignond_session_data_set_username(response, username);
235 gsignond_dictionary_set_string(response, "CNonce", cnonce);
237 gsignond_dictionary_set_string(response, "Response", digest);
240 gsignond_plugin_response_final(plugin, response);
241 gsignond_dictionary_unref(response);
245 gsignond_digest_plugin_request_initial (
246 GSignondPlugin *plugin,
247 GSignondSessionData *session_data,
248 GSignondDictionary *identity_method_cache,
249 const gchar *mechanism)
251 g_return_if_fail (plugin != NULL);
252 g_return_if_fail (GSIGNOND_IS_DIGEST_PLUGIN (plugin));
254 GSignondDigestPlugin *self = GSIGNOND_DIGEST_PLUGIN (plugin);
255 GSignondDigestPluginPrivate *priv = self->priv;
257 g_return_if_fail (priv != NULL);
259 const gchar *username = gsignond_session_data_get_username(session_data);
260 const gchar *secret = gsignond_session_data_get_secret(session_data);
262 if (username != NULL && secret != NULL) {
263 _gsignond_digest_plugin_return_digest(plugin, username, secret, session_data);
267 if (priv->session_data) {
268 gsignond_dictionary_unref (priv->session_data);
269 priv->session_data = NULL;
271 gsignond_dictionary_ref (session_data);
272 priv->session_data = session_data;
274 GSignondSignonuiData *user_action_data = gsignond_dictionary_new ();
275 DATA_SET_VALUE (user_action_data, "Realm",
276 gsignond_session_data_get_realm (session_data));
277 DATA_SET_VALUE (user_action_data, "DigestUri",
278 gsignond_dictionary_get_string (session_data, "DigestUri"));
279 gsignond_signonui_data_set_query_username (user_action_data, TRUE);
280 gsignond_signonui_data_set_query_password (user_action_data, TRUE);
281 gsignond_plugin_user_action_required (plugin, user_action_data);
282 gsignond_dictionary_unref (user_action_data);
286 gsignond_digest_plugin_user_action_finished (
287 GSignondPlugin *plugin,
288 GSignondSignonuiData *signonui_data)
290 g_return_if_fail (plugin != NULL);
291 g_return_if_fail (GSIGNOND_IS_DIGEST_PLUGIN (plugin));
293 GSignondDigestPlugin *self = GSIGNOND_DIGEST_PLUGIN (plugin);
294 GSignondDigestPluginPrivate *priv = self->priv;
295 g_return_if_fail (priv != NULL);
297 GSignondSessionData *session_data = NULL;
298 GSignondSignonuiError query_error;
299 gboolean res = gsignond_signonui_data_get_query_error(signonui_data,
302 GError* error = g_error_new(GSIGNOND_ERROR,
303 GSIGNOND_ERROR_USER_INTERACTION,
304 "userActionFinished did not return an error value");
305 gsignond_plugin_error (plugin, error);
309 const gchar* username = gsignond_signonui_data_get_username(signonui_data);
310 const gchar* secret = gsignond_signonui_data_get_password(signonui_data);
312 session_data = priv->session_data;
314 if (query_error == SIGNONUI_ERROR_NONE &&
317 session_data != NULL) {
318 _gsignond_digest_plugin_return_digest(plugin, username, secret, session_data);
319 } else if (query_error == SIGNONUI_ERROR_CANCELED) {
320 gsignond_digest_plugin_cancel (plugin);
322 GError* error = g_error_new(GSIGNOND_ERROR,
323 GSIGNOND_ERROR_USER_INTERACTION, "userActionFinished error: %d",
325 gsignond_plugin_error (plugin, error);
331 gsignond_digest_plugin_refresh (
332 GSignondPlugin *self,
333 GSignondSessionData *session_data)
335 gsignond_plugin_refreshed(self, session_data);
339 gsignond_plugin_interface_init (GSignondPluginInterface *iface)
341 iface->cancel = gsignond_digest_plugin_cancel;
342 iface->request_initial = gsignond_digest_plugin_request_initial;
343 iface->request = gsignond_digest_plugin_request;
344 iface->user_action_finished = gsignond_digest_plugin_user_action_finished;
345 iface->refresh = gsignond_digest_plugin_refresh;
349 gsignond_digest_plugin_init (GSignondDigestPlugin *self)
351 GSignondDigestPluginPrivate *priv =
352 GSIGNOND_DIGEST_PLUGIN_GET_PRIVATE (self);
355 priv->session_data = NULL;
367 gsignond_digest_plugin_set_property (
376 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
382 gsignond_digest_plugin_get_property (
388 GSignondDigestPlugin *digest_plugin = GSIGNOND_DIGEST_PLUGIN (object);
389 (void) digest_plugin;
390 gchar *mechanisms[] = { "digest", NULL };
395 g_value_set_string (value, "digest");
397 case PROP_MECHANISMS:
398 g_value_set_boxed (value, mechanisms);
401 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
407 gsignond_digest_plugin_dispose (GObject *gobject)
409 g_return_if_fail (GSIGNOND_IS_DIGEST_PLUGIN (gobject));
410 GSignondDigestPlugin *self = GSIGNOND_DIGEST_PLUGIN (gobject);
411 g_return_if_fail (self->priv != NULL);
413 if (self->priv->session_data) {
414 gsignond_dictionary_unref (self->priv->session_data);
415 self->priv->session_data = NULL;
418 /* Chain up to the parent class */
419 G_OBJECT_CLASS (gsignond_digest_plugin_parent_class)->dispose (
424 gsignond_digest_plugin_class_init (GSignondDigestPluginClass *klass)
426 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
428 gobject_class->set_property = gsignond_digest_plugin_set_property;
429 gobject_class->get_property = gsignond_digest_plugin_get_property;
430 gobject_class->dispose = gsignond_digest_plugin_dispose;
432 g_object_class_override_property (gobject_class, PROP_TYPE, "type");
433 g_object_class_override_property (gobject_class, PROP_MECHANISMS,
436 g_type_class_add_private (klass, sizeof (GSignondDigestPluginPrivate));