2 * @file sip-sec-digest.c
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
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
,
41 const gchar
*password
)
43 /* H(A1): H(user ":" realm ":" password) */
44 gchar
*string
= g_strdup_printf("%s:%s:%s", user
, realm
, password
);
46 guchar digest
[SIPE_DIGEST_MD5_LENGTH
];
47 sipe_digest_md5((guchar
*)string
, strlen(string
), digest
);
50 /* Result: LOWER(HEXSTRING(H(A1))) */
51 string
= buff_to_hex_str(digest
, sizeof(digest
));
52 HA1
= g_ascii_strdown(string
, -1);
57 static gchar
*digest_HA2(const gchar
*method
,
60 /* H(A2): H(method ":" target) */
61 gchar
*string
= g_strdup_printf("%s:%s", method
, target
);
63 guchar digest
[SIPE_DIGEST_MD5_LENGTH
];
64 sipe_digest_md5((guchar
*)string
, strlen(string
), digest
);
67 /* Result: LOWER(HEXSTRING(H(A1))) */
68 string
= buff_to_hex_str(digest
, sizeof(digest
));
69 HA2
= g_ascii_strdown(string
, -1);
74 static gchar
*generate_cnonce(void)
76 #ifdef SIP_SEC_DIGEST_COMPILING_TEST
77 return(g_strdup(cnonce_fixed
));
79 return(g_strdup_printf("%04x%04x", rand() & 0xFFFF, rand() & 0xFFFF));
83 static gchar
*digest_response(const gchar
*user
,
85 const gchar
*password
,
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
);
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
);
107 sipe_digest_md5((guchar
*)string
, strlen(string
), digest
);
110 /* Result: LOWER(HEXSTRING(H(A1))) */
111 string
= buff_to_hex_str(digest
, sizeof(digest
));
112 Digest
= g_ascii_strdown(string
, -1);
117 gchar
*sip_sec_digest_authorization(struct sipe_core_private
*sipe_private
,
124 gchar
*opaque
= NULL
;
126 gchar
*authorization
= NULL
;
129 if (!sipe_private
->password
)
132 /* skip white space */
133 while (*header
== ' ')
136 /* start of next parameter value */
137 while ((param
= strchr(header
, '=')) != NULL
) {
140 /* parameter value type */
143 /* string: xyz="..."(,) */
144 end
= strchr(++param
, '"');
146 SIPE_DEBUG_ERROR("sip_sec_digest_authorization: corrupted string parameter near '%s'", header
);
150 /* number: xyz=12345(,) */
151 end
= strchr(param
, ',');
154 end
= param
+ strlen(param
);
159 if (g_str_has_prefix(header
, "nonce=\"")) {
161 nonce
= g_strndup(param
, end
- param
);
162 } else if (g_str_has_prefix(header
, "opaque=\"")) {
164 opaque
= g_strndup(param
, end
- param
);
165 } else if (g_str_has_prefix(header
, "realm=\"")) {
167 realm
= g_strndup(param
, end
- param
);
170 /* skip to next parameter */
171 while ((*end
== '"') || (*end
== ',') || (*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
,
183 sipe_private
->password
,
191 #ifdef SIP_SEC_DIGEST_COMPILING_TEST
192 SIPE_DEBUG_INFO("RES %s", response
);
195 authorization
= g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", qop=auth, nc=%s, cnonce=\"%s\", %sresponse=\"%s\"",
209 SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_digest_authorization: no digest parameters found. Giving up.");
215 return(authorization
);