update ChangeLog
[siplcs.git] / src / core / sip-sec-digest.c
blob41986854d2b8cc92f2a61d828ebf71f3a2c2ad12
1 /**
2 * @file sip-sec-digest.c
4 * pidgin-sipe
6 * Copyright (C) 2013 SIPE Project <http://sipe.sourceforge.net/>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include <stdlib.h>
25 #include <string.h>
27 #include <glib.h>
29 #include "sip-sec-digest.h"
30 #include "sipe-backend.h"
31 #include "sipe-core.h"
32 #include "sipe-core-private.h"
33 #include "sipe-digest.h"
34 #include "sipe-utils.h"
37 * Calculate a response for HTTP MD5 Digest authentication (RFC 2617)
39 static gchar *digest_HA1(const gchar *user,
40 const gchar *realm,
41 const gchar *password)
43 /* H(A1): H(user ":" realm ":" password) */
44 gchar *string = g_strdup_printf("%s:%s:%s", user, realm, password);
45 gchar *HA1;
46 guchar digest[SIPE_DIGEST_MD5_LENGTH];
47 sipe_digest_md5((guchar *)string, strlen(string), digest);
48 g_free(string);
50 /* Result: LOWER(HEXSTRING(H(A1))) */
51 string = buff_to_hex_str(digest, sizeof(digest));
52 HA1 = g_ascii_strdown(string, -1);
53 g_free(string);
54 return(HA1);
57 static gchar *digest_HA2(const gchar *method,
58 const gchar *target)
60 /* H(A2): H(method ":" target) */
61 gchar *string = g_strdup_printf("%s:%s", method, target);
62 gchar *HA2;
63 guchar digest[SIPE_DIGEST_MD5_LENGTH];
64 sipe_digest_md5((guchar *)string, strlen(string), digest);
65 g_free(string);
67 /* Result: LOWER(HEXSTRING(H(A1))) */
68 string = buff_to_hex_str(digest, sizeof(digest));
69 HA2 = g_ascii_strdown(string, -1);
70 g_free(string);
71 return(HA2);
74 static gchar *generate_cnonce(void)
76 #ifdef SIP_SEC_DIGEST_COMPILING_TEST
77 return(g_strdup(cnonce_fixed));
78 #else
79 return(g_strdup_printf("%04x%04x", rand() & 0xFFFF, rand() & 0xFFFF));
80 #endif
83 static gchar *digest_response(const gchar *user,
84 const gchar *realm,
85 const gchar *password,
86 const gchar *nonce,
87 const gchar *nc,
88 const gchar *cnonce,
89 const gchar *qop,
90 const gchar *method,
91 const gchar *target)
93 gchar *HA1 = digest_HA1(user, realm, password);
94 gchar *HA2 = digest_HA2(method, target);
95 gchar *string, *Digest;
96 guchar digest[SIPE_DIGEST_MD5_LENGTH];
98 #ifdef SIP_SEC_DIGEST_COMPILING_TEST
99 SIPE_DEBUG_INFO("HA1 %s", HA1);
100 SIPE_DEBUG_INFO("HA2 %s", HA2);
101 #endif
103 /* Digest: H(H(A1) ":" nonce ":" nc ":" cnonce ":" qop ":" H(A2) */
104 string = g_strdup_printf("%s:%s:%s:%s:%s:%s", HA1, nonce, nc, cnonce, qop, HA2);
105 g_free(HA2);
106 g_free(HA1);
107 sipe_digest_md5((guchar *)string, strlen(string), digest);
108 g_free(string);
110 /* Result: LOWER(HEXSTRING(H(A1))) */
111 string = buff_to_hex_str(digest, sizeof(digest));
112 Digest = g_ascii_strdown(string, -1);
113 g_free(string);
114 return(Digest);
117 gchar *sip_sec_digest_authorization(struct sipe_core_private *sipe_private,
118 const gchar *header,
119 const gchar *method,
120 const gchar *target)
122 const gchar *param;
123 gchar *nonce = NULL;
124 gchar *opaque = NULL;
125 gchar *realm = NULL;
126 gchar *authorization = NULL;
128 /* sanity checks */
129 if (!sipe_private->password)
130 return(NULL);
132 /* skip white space */
133 while (*header == ' ')
134 header++;
136 /* start of next parameter value */
137 while ((param = strchr(header, '=')) != NULL) {
138 const gchar *end;
140 /* parameter value type */
141 param++;
142 if (*param == '"') {
143 /* string: xyz="..."(,) */
144 end = strchr(++param, '"');
145 if (!end) {
146 SIPE_DEBUG_ERROR("sip_sec_digest_authorization: corrupted string parameter near '%s'", header);
147 break;
149 } else {
150 /* number: xyz=12345(,) */
151 end = strchr(param, ',');
152 if (!end) {
153 /* last parameter */
154 end = param + strlen(param);
158 /* parameter type */
159 if (g_str_has_prefix(header, "nonce=\"")) {
160 g_free(nonce);
161 nonce = g_strndup(param, end - param);
162 } else if (g_str_has_prefix(header, "opaque=\"")) {
163 g_free(opaque);
164 opaque = g_strndup(param, end - param);
165 } else if (g_str_has_prefix(header, "realm=\"")) {
166 g_free(realm);
167 realm = g_strndup(param, end - param);
170 /* skip to next parameter */
171 while ((*end == '"') || (*end == ',') || (*end == ' '))
172 end++;
173 header = end;
176 if (nonce && realm) {
177 const gchar *authuser = sipe_private->authuser ? sipe_private->authuser : sipe_private->username;
178 const gchar *nc = "00000001";
179 gchar *cnonce = generate_cnonce();
180 gchar *opt_opaque = opaque ? g_strdup_printf("opaque=\"%s\", ", opaque) : g_strdup("");
181 gchar *response = digest_response(authuser,
182 realm,
183 sipe_private->password,
184 nonce,
186 cnonce,
187 "auth",
188 method,
189 target);
191 #ifdef SIP_SEC_DIGEST_COMPILING_TEST
192 SIPE_DEBUG_INFO("RES %s", response);
193 #endif
195 authorization = g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", qop=auth, nc=%s, cnonce=\"%s\", %sresponse=\"%s\"",
196 authuser,
197 realm,
198 nonce,
199 target,
201 cnonce,
202 opt_opaque,
203 response);
204 g_free(response);
205 g_free(opt_opaque);
206 g_free(cnonce);
208 } else
209 SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_digest_authorization: no digest parameters found. Giving up.");
211 g_free(realm);
212 g_free(opaque);
213 g_free(nonce);
215 return(authorization);
219 Local Variables:
220 mode: c
221 c-file-style: "bsd"
222 indent-tabs-mode: t
223 tab-width: 8
224 End: