1 /*
2 * This file is part of the Sofia-SIP package
3 *
4 * Copyright (C) 2005 Nokia Corporation.
5 *
6 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 *
23 */
24
25 /**@CFILE auth_plugin_ntlm.c
26 *
27 * @brief Plugin for delayed authentication.
28 *
29 * This authentication plugin provides authentication operation that is
30 * intentionally delayed. It serves as an example of server-side
31 * authentication plugins.
32 *
33 * @author Pekka Pessi <Pekka.Pessi@nokia.com>.
34 *
35 * @date Created: Wed Apr 11 15:14:03 2001 ppessi
36 */
37
38 #include "config.h"
39
40 #include <stddef.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <stdio.h>
44 #include <assert.h>
45
46 #include <sofia-sip/su_debug.h>
47 #include <sofia-sip/su_wait.h>
48
49 #include <sofia-sip/su_alloc.h>
50 #include <sofia-sip/su_tagarg.h>
51
52 #include "sofia-sip/auth_module.h"
53 #include "sofia-sip/auth_plugin.h"
54 #include "sofia-sip/auth_ntlm.h"
55
56 #if HAVE_FUNC
57 #elif HAVE_FUNCTION
58 #define __func__ __FUNCTION__
59 #else
60 static char const __func__[] = "auth_plugin_ntml";
61 #endif
62
63 /* ====================================================================== */
64 /* NTLM authentication scheme */
65
66 static int auth_init_ntlm(auth_mod_t *am,
67 auth_scheme_t *base,
68 su_root_t *root,
69 tag_type_t tag, tag_value_t value, ...);
70
71 static void auth_method_ntlm_x(auth_mod_t *am,
72 auth_status_t *as,
73 msg_auth_t *au,
74 auth_challenger_t const *ach);
75
76 auth_scheme_t auth_scheme_ntlm[1] =
77 {{
78 "NTLM", /* asch_method */
79 sizeof (auth_mod_t), /* asch_size */
80 auth_init_default, /* asch_init */
81 auth_method_ntlm_x, /* asch_check */
82 auth_challenge_ntlm, /* asch_challenge */
83 auth_cancel_default, /* asch_cancel */
84 auth_destroy_default /* asch_destroy */
85 }};
86
87 #define AUTH_NTLM_NONCE_LEN (BASE64_SIZE(sizeof (struct nonce)) + 1)
88
auth_init_ntlm(auth_mod_t * am,auth_scheme_t * base,su_root_t * root,tag_type_t tag,tag_value_t value,...)89 static int auth_init_ntlm(auth_mod_t *am,
90 auth_scheme_t *base,
91 su_root_t *root,
92 tag_type_t tag, tag_value_t value, ...)
93 {
94 auth_plugin_t *ap = AUTH_PLUGIN(am);
95 int retval = -1;
96 ta_list ta;
97
98 ta_start(ta, tag, value);
99
100 if (auth_init_default(am, NULL, root, ta_tags(ta)) != -1) {
101 retval = 0;
102 }
103
104 ta_end(ta);
105
106 return retval;
107 }
108
109
110 /** Authenticate a request with @b NTLM authentication scheme.
111 *
112 * This function reads user database before authentication, if needed.
113 */
114 static
auth_method_ntlm_x(auth_mod_t * am,auth_status_t * as,msg_auth_t * au,auth_challenger_t const * ach)115 void auth_method_ntlm_x(auth_mod_t *am,
116 auth_status_t *as,
117 msg_auth_t *au,
118 auth_challenger_t const *ach)
119 {
120 if (am) {
121 auth_readdb_if_needed(am);
122 auth_method_ntlm(am, as, au, ach);
123 }
124 }
125
126 /** Authenticate a request with @b Ntlm authentication scheme.
127 */
auth_method_ntlm(auth_mod_t * am,auth_status_t * as,msg_auth_t * au,auth_challenger_t const * ach)128 void auth_method_ntlm(auth_mod_t *am,
129 auth_status_t *as,
130 msg_auth_t *au,
131 auth_challenger_t const *ach)
132 {
133 as->as_allow = as->as_allow || auth_allow_check(am, as) == 0;
134
135 if (as->as_realm)
136 au = auth_ntlm_credentials(au, as->as_realm, am->am_opaque,
137 am->am_gssapi_data, am->am_targetname);
138
139 else
140 au = NULL;
141
142 if (as->as_allow) {
143 SU_DEBUG_5(("%s: allow unauthenticated %s\n", __func__, as->as_method));
144 as->as_status = 0, as->as_phrase = NULL;
145 as->as_match = (msg_header_t *)au;
146 return;
147 }
148
149 if (au) {
150 auth_response_t ar[1] = {{ sizeof(ar) }};
151 auth_ntlm_response_get(as->as_home, ar, au->au_params);
152 as->as_match = (msg_header_t *)au;
153 auth_check_ntlm(am, as, ar, ach);
154 }
155 else {
156 /* There was no matching credentials, send challenge */
157 SU_DEBUG_5(("%s: no credentials matched\n", __func__));
158 auth_challenge_ntlm(am, as, ach);
159 }
160 }
161
162
163 /** Find a NTLM credential header with matching realm and opaque. */
auth_ntlm_credentials(msg_auth_t * auth,char const * realm,char const * opaque,char const * gssapidata,char const * targetname)164 msg_auth_t *auth_ntlm_credentials(msg_auth_t *auth,
165 char const *realm,
166 char const *opaque,
167 char const *gssapidata,
168 char const *targetname)
169 {
170 char const *agssapidata, *atargetname;
171
172 for (;auth; auth = auth_mod_credentials(auth->au_next)) {
173 if (!su_casematch(auth->au_scheme, "NTLM"))
174 continue;
175
176 if (gssapidata) {
177 agssapidata = msg_header_find_param(auth->au_common, "gssapi-data=");
178 if (!agssapidata || auth_strcmp(agssapidata, gssapidata))
179 continue;
180 }
181
182 if (targetname) {
183 atargetname = msg_header_find_param(auth->au_common, "targetname=");
184 if (!atargetname || auth_strcmp(atargetname, targetname))
185 continue;
186 }
187
188 return auth;
189 }
190
191 return NULL;
192 }
193
194
195 /** Check ntlm authentication */
auth_check_ntlm(auth_mod_t * am,auth_status_t * as,auth_response_t * ar,auth_challenger_t const * ach)196 void auth_check_ntlm(auth_mod_t *am,
197 auth_status_t *as,
198 auth_response_t *ar,
199 auth_challenger_t const *ach)
200 {
201 char const *a1;
202 auth_hexmd5_t a1buf, response;
203 auth_passwd_t *apw;
204 char const *phrase;
205 msg_time_t now = msg_now();
206
207 if (am == NULL || as == NULL || ar == NULL || ach == NULL) {
208 if (as) {
209 as->as_status = 500, as->as_phrase = "Internal Server Error";
210 as->as_response = NULL;
211 }
212 return;
213 }
214
215 phrase = "Bad authorization";
216
217 #define PA "Authorization missing "
218
219 if ((!ar->ar_username && (phrase = PA "username")) ||
220 (!ar->ar_nonce && (phrase = PA "nonce")) ||
221 (!ar->ar_uri && (phrase = PA "URI")) ||
222 (!ar->ar_response && (phrase = PA "response")) ||
223 /* (!ar->ar_opaque && (phrase = PA "opaque")) || */
224 /* Check for qop */
225 (ar->ar_qop &&
226 ((ar->ar_auth &&
227 !su_casematch(ar->ar_qop, "auth") &&
228 !su_casematch(ar->ar_qop, "\"auth\"")) ||
229 (ar->ar_auth_int &&
230 !su_casematch(ar->ar_qop, "auth-int") &&
231 !su_casematch(ar->ar_qop, "\"auth-int\"")))
232 && (phrase = PA "has invalid qop"))) {
233 assert(phrase);
234 SU_DEBUG_5(("auth_method_ntlm: 400 %s\n", phrase));
235 as->as_status = 400, as->as_phrase = phrase;
236 as->as_response = NULL;
237 return;
238 }
239
240 /* XXX - replace */
241 #if 0
242 if (as->as_nonce_issued == 0 /* Already validated nonce */ &&
243 auth_validate_ntlm_nonce(am, as, ar, now) < 0) {
244 #else
245 if (as->as_nonce_issued == 0 /* Already validated nonce */ &&
246 auth_validate_digest_nonce(am, as, ar, now) < 0) {
247 #endif
248 as->as_blacklist = am->am_blacklist;
249 auth_challenge_ntlm(am, as, ach);
250 return;
251 }
252
253 if (as->as_stale) {
254 auth_challenge_ntlm(am, as, ach);
255 return;
256 }
257
258 apw = auth_mod_getpass(am, ar->ar_username, ar->ar_realm);
259
260 #if 0
261 if (apw && apw->apw_hash)
262 a1 = apw->apw_hash;
263 else if (apw && apw->apw_pass)
264 auth_ntlm_a1(ar, a1buf, apw->apw_pass), a1 = a1buf;
265 else
266 auth_ntlm_a1(ar, a1buf, "xyzzy"), a1 = a1buf, apw = NULL;
267
268 if (ar->ar_md5sess)
269 auth_ntlm_a1sess(ar, a1buf, a1), a1 = a1buf;
270 #else
271 if (apw && apw->apw_hash)
272 a1 = apw->apw_hash;
273 else if (apw && apw->apw_pass)
274 auth_digest_a1(ar, a1buf, apw->apw_pass), a1 = a1buf;
275 else
276 auth_digest_a1(ar, a1buf, "xyzzy"), a1 = a1buf, apw = NULL;
277
278 if (ar->ar_md5sess)
279 auth_digest_a1sess(ar, a1buf, a1), a1 = a1buf;
280 #endif
281
282 /* XXX - replace with auth_ntlm_response */
283 #if 0
284 auth_ntlm_response(ar, response, a1,
285 as->as_method, as->as_body, as->as_bodylen);
286 #else
287 auth_digest_response(ar, response, a1,
288 as->as_method, as->as_body, as->as_bodylen);
289 #endif
290
291 if (!apw || strcmp(response, ar->ar_response)) {
292 if (am->am_forbidden) {
293 as->as_status = 403, as->as_phrase = "Forbidden";
294 as->as_blacklist = am->am_blacklist;
295 as->as_response = NULL;
296 }
297 else {
298 auth_challenge_ntlm(am, as, ach);
299 as->as_blacklist = am->am_blacklist;
300 }
301 SU_DEBUG_5(("auth_method_ntlm: response did not match\n"));
302
303 return;
304 }
305
306 assert(apw);
307
308 as->as_user = apw->apw_user;
309 as->as_anonymous = apw == am->am_anon_user;
310
311 if (am->am_nextnonce || am->am_mutual)
312 auth_info_ntlm(am, as, ach);
313
314 if (am->am_challenge)
315 auth_challenge_ntlm(am, as, ach);
316
317 SU_DEBUG_7(("auth_method_ntlm: successful authentication\n"));
318
319 as->as_status = 0; /* Successful authentication! */
320 as->as_phrase = "";
321 }
322
323 /** Construct a challenge header for @b Ntlm authentication scheme. */
324 void auth_challenge_ntlm(auth_mod_t *am,
325 auth_status_t *as,
326 auth_challenger_t const *ach)
327 {
328 char const *u, *d;
329 char nonce[AUTH_NTLM_NONCE_LEN];
330
331 #if 0
332 auth_generate_ntlm_nonce(am, nonce, sizeof nonce, 0, msg_now());
333 #else
334 auth_generate_digest_nonce(am, nonce, sizeof nonce, 0, msg_now());
335 #endif
336
337 u = as->as_uri;
338 d = as->as_pdomain;
339
340 as->as_response =
341 msg_header_format(as->as_home, ach->ach_header,
342 "Ntlm"
343 " realm=\"%s\","
344 "%s%s%s"
345 "%s%s%s"
346 " nonce=\"%s\","
347 "%s%s%s"
348 "%s" /* stale */
349 " algorithm=%s"
350 "%s%s%s",
351 as->as_realm,
352 u ? " uri=\"" : "", u ? u : "", u ? "\"," : "",
353 d ? " domain=\"" : "", d ? d : "", d ? "\"," : "",
354 nonce,
355 am->am_opaque ? " opaque=\"" : "",
356 am->am_opaque ? am->am_opaque : "",
357 am->am_opaque ? "\"," : "",
358 as->as_stale ? " stale=true," : "",
359 am->am_algorithm,
360 am->am_qop ? ", qop=\"" : "",
361 am->am_qop ? am->am_qop : "",
362 am->am_qop ? "\"" : "");
363
364 if (!as->as_response)
365 as->as_status = 500, as->as_phrase = auth_internal_server_error;
366 else
367 as->as_status = ach->ach_status, as->as_phrase = ach->ach_phrase;
368 }
369
370 /** Construct a info header for @b Ntlm authentication scheme. */
371 void auth_info_ntlm(auth_mod_t *am,
372 auth_status_t *as,
373 auth_challenger_t const *ach)
374 {
375 if (!ach->ach_info)
376 return;
377
378 if (am->am_nextnonce) {
379 char nonce[AUTH_NTLM_NONCE_LEN];
380
381 /* XXX - replace */
382 #if 0
383 auth_generate_ntlm_nonce(am, nonce, sizeof nonce, 1, msg_now());
384 #else
385 auth_generate_digest_nonce(am, nonce, sizeof nonce, 1, msg_now());
386 #endif
387
388 as->as_info =
389 msg_header_format(as->as_home, ach->ach_info, "nextnonce=\"%s\"", nonce);
390 }
391 }
392