1 martin 1.43 //%LICENSE////////////////////////////////////////////////////////////////
|
2 martin 1.44 //
|
3 martin 1.43 // Licensed to The Open Group (TOG) under one or more contributor license
4 // agreements. Refer to the OpenPegasusNOTICE.txt file distributed with
5 // this work for additional information regarding copyright ownership.
6 // Each contributor licenses this file to you under the OpenPegasus Open
7 // Source License; you may not use this file except in compliance with the
8 // License.
|
9 martin 1.44 //
|
10 martin 1.43 // Permission is hereby granted, free of charge, to any person obtaining a
11 // copy of this software and associated documentation files (the "Software"),
12 // to deal in the Software without restriction, including without limitation
13 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 // and/or sell copies of the Software, and to permit persons to whom the
15 // Software is furnished to do so, subject to the following conditions:
|
16 martin 1.44 //
|
17 martin 1.43 // The above copyright notice and this permission notice shall be included
18 // in all copies or substantial portions of the Software.
|
19 martin 1.44 //
|
20 martin 1.43 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
21 martin 1.44 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22 martin 1.43 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27 martin 1.44 //
|
28 martin 1.43 //////////////////////////////////////////////////////////////////////////
|
29 mike 1.2 //
30 //%/////////////////////////////////////////////////////////////////////////////
31
32 #include <Pegasus/Common/Config.h>
33 #include <Pegasus/Common/System.h>
34 #include <Pegasus/Common/FileSystem.h>
|
35 kumpf 1.4 #include <Pegasus/Common/Base64.h>
|
36 chuck 1.15 #include <Pegasus/Common/Exception.h>
|
37 kumpf 1.18 #include <Pegasus/Common/Constants.h>
|
38 mike 1.2 #include "ClientAuthenticator.h"
39
|
40 chip 1.7 #include <ctype.h>
|
41 kumpf 1.6
|
42 kumpf 1.4 //
|
43 kumpf 1.6 // Constants used to parse the authentication challenge header
|
44 kumpf 1.4 //
|
45 kumpf 1.6 #define CHAR_BLANK ' '
46
47 #define CHAR_QUOTE '"'
48
|
49 kumpf 1.4
|
50 mike 1.2 PEGASUS_USING_STD;
51
52 PEGASUS_NAMESPACE_BEGIN
53
|
54 kumpf 1.4 /**
|
55 kumpf 1.41 Constant representing the authentication challenge header.
|
56 kumpf 1.4 */
|
57 kumpf 1.45 static const char* WWW_AUTHENTICATE = "WWW-Authenticate";
|
58 kumpf 1.4
59 /**
60 Constant representing the Basic authentication header.
61 */
|
62 kumpf 1.41 static const String BASIC_AUTH_HEADER = "Authorization: Basic ";
|
63 kumpf 1.4
64 /**
65 Constant representing the Digest authentication header.
66 */
|
67 kumpf 1.41 static const String DIGEST_AUTH_HEADER = "Authorization: Digest ";
|
68 kumpf 1.4
69 /**
70 Constant representing the local authentication header.
71 */
|
72 kumpf 1.41 static const String LOCAL_AUTH_HEADER = "PegasusAuthorization: Local";
|
73 kumpf 1.4
74
|
75 kumpf 1.21 ClientAuthenticator::ClientAuthenticator()
|
76 mike 1.2 {
|
77 kumpf 1.24 clear();
|
78 mike 1.2 }
79
80 ClientAuthenticator::~ClientAuthenticator()
81 {
82 }
83
|
84 kumpf 1.24 void ClientAuthenticator::clear()
|
85 mike 1.2 {
|
86 kumpf 1.30 _requestMessage.reset();
|
87 kumpf 1.41 _userName.clear();
88 _password.clear();
89 _localAuthFile.clear();
90 _localAuthFileContent.clear();
|
91 kumpf 1.24 _challengeReceived = false;
92 _authType = ClientAuthenticator::NONE;
|
93 mike 1.2 }
94
|
95 kumpf 1.4 Boolean ClientAuthenticator::checkResponseHeaderForChallenge(
96 Array<HTTPHeader> headers)
|
97 mike 1.2 {
98 //
99 // Search for "WWW-Authenticate" header:
100 //
|
101 kumpf 1.45 const char* authHeader;
|
102 kumpf 1.6 String authType;
103 String authRealm;
|
104 mike 1.2
105 if (!HTTPMessage::lookupHeader(
|
106 kumpf 1.45 headers, WWW_AUTHENTICATE, authHeader, false))
|
107 mike 1.2 {
108 return false;
109 }
110
|
111 kumpf 1.10 if (_challengeReceived)
|
112 kumpf 1.6 {
|
113 kumpf 1.23 // Do not respond to a challenge more than once
114 return false;
|
115 kumpf 1.6 }
|
116 kumpf 1.4 else
117 {
|
118 kumpf 1.10 _challengeReceived = true;
119
120 //
121 // Parse the authentication challenge header
122 //
|
123 kumpf 1.36 if (!_parseAuthHeader(authHeader, authType, authRealm))
|
124 kumpf 1.10 {
125 throw InvalidAuthHeader();
126 }
127
|
128 kumpf 1.40 if (String::equal(authType, "Local"))
|
129 kumpf 1.10 {
130 _authType = ClientAuthenticator::LOCAL;
131 }
132 else if ( String::equal(authType, "Basic"))
133 {
134 _authType = ClientAuthenticator::BASIC;
135 }
136 else if ( String::equal(authType, "Digest"))
137 {
138 _authType = ClientAuthenticator::DIGEST;
139 }
140 else
141 {
142 throw InvalidAuthHeader();
143 }
|
144 kumpf 1.4
|
145 kumpf 1.40 if (_authType == ClientAuthenticator::LOCAL)
|
146 kumpf 1.18 {
147 String filePath = authRealm;
148 FileSystem::translateSlashes(filePath);
149
150 // Check whether the directory is a valid pre-defined directory.
151 //
152 Uint32 index = filePath.reverseFind('/');
153
154 if (index != PEG_NOT_FOUND)
155 {
156 String dirName = filePath.subString(0,index);
157
158 if (!String::equal(dirName, String(PEGASUS_LOCAL_AUTH_DIR)))
159 {
|
160 kumpf 1.23 // Refuse to respond to the challenge when the file is
161 // not in the expected directory
162 return false;
|
163 kumpf 1.18 }
164 }
|
165 kumpf 1.41
166 _localAuthFile = authRealm;
|
167 kumpf 1.18 }
168
|
169 kumpf 1.10 return true;
170 }
|
171 mike 1.2 }
172
173
174 String ClientAuthenticator::buildRequestAuthHeader()
175 {
|
176 kumpf 1.41 String challengeResponse;
|
177 kumpf 1.6
|
178 mike 1.2 switch (_authType)
179 {
|
180 kumpf 1.4 case ClientAuthenticator::BASIC:
181
182 if (_challengeReceived)
183 {
|
184 kumpf 1.6 challengeResponse = BASIC_AUTH_HEADER;
185
|
186 kumpf 1.4 //
187 // build the credentials string using the
188 // user name and password
189 //
190 String userPass = _userName;
191
192 userPass.append(":");
193
194 userPass.append(_password);
195
196 //
197 // copy userPass string content to Uint8 array for encoding
198 //
|
199 mike 1.31 Buffer userPassArray;
|
200 kumpf 1.4
201 Uint32 userPassLength = userPass.size();
202
|
203 kumpf 1.36 userPassArray.reserveCapacity(userPassLength);
|
204 kumpf 1.4 userPassArray.clear();
205
|
206 kumpf 1.36 for (Uint32 i = 0; i < userPassLength; i++)
|
207 kumpf 1.4 {
|
208 kumpf 1.36 userPassArray.append((char)userPass[i]);
|
209 kumpf 1.4 }
210
211 //
212 // base64 encode the user name and password
213 //
|
214 mike 1.31 Buffer encodedArray;
|
215 kumpf 1.4
|
216 kumpf 1.36 encodedArray = Base64::encode(userPassArray);
|
217 kumpf 1.4
|
218 kumpf 1.6 challengeResponse.append(
|
219 kumpf 1.39 String(encodedArray.getData(), encodedArray.size()));
|
220 kumpf 1.4 }
221 break;
222
|
223 chip 1.7 //
|
224 kumpf 1.4 //ATTN: Implement Digest Auth challenge handling code here
|
225 chip 1.7 //
|
226 kumpf 1.9 case ClientAuthenticator::DIGEST:
|
227 chip 1.7 // if (_challengeReceived)
|
228 mike 1.2 // {
|
229 kumpf 1.6 // challengeResponse = DIGEST_AUTH_HEADER;
|
230 mike 1.2 // }
|
231 kumpf 1.9 break;
|
232 mike 1.2
|
233 kumpf 1.6 case ClientAuthenticator::LOCAL:
|
234 mike 1.2
|
235 kumpf 1.6 challengeResponse = LOCAL_AUTH_HEADER;
236 challengeResponse.append(" \"");
|
237 mike 1.2
|
238 chip 1.7 if (_userName.size())
|
239 kumpf 1.6 {
240 challengeResponse.append(_userName);
241 }
242 else
243 {
|
244 mike 1.2 //
|
245 kumpf 1.6 // Get the current login user name
|
246 mike 1.2 //
|
247 kumpf 1.12 challengeResponse.append(System::getEffectiveUserName());
|
248 mike 1.2 }
|
249 kumpf 1.6
250 challengeResponse.append(_buildLocalAuthResponse());
|
251 mike 1.2
252 break;
253
|
254 kumpf 1.9 case ClientAuthenticator::NONE:
|
255 chip 1.7 //
|
256 kumpf 1.4 // Gets here only when no authType was set.
|
257 chip 1.7 //
|
258 kumpf 1.6 challengeResponse.clear();
|
259 mike 1.2 break;
|
260 kumpf 1.9
261 default:
262 PEGASUS_ASSERT(0);
263 break;
|
264 mike 1.2 }
265
|
266 kumpf 1.6 return (challengeResponse);
|
267 mike 1.2 }
268
269 void ClientAuthenticator::setRequestMessage(Message* message)
270 {
|
271 kumpf 1.30 _requestMessage.reset(message);
|
272 mike 1.2 }
273
274 Message* ClientAuthenticator::getRequestMessage()
275 {
|
276 kumpf 1.30 return _requestMessage.get();
277 }
|
278 chip 1.7
|
279 kumpf 1.41 void ClientAuthenticator::resetChallengeStatus()
|
280 mateus.baur 1.32 {
281 _challengeReceived = false;
|
282 kumpf 1.41 _localAuthFile.clear();
283 _localAuthFileContent.clear();
|
284 mateus.baur 1.32 }
285
|
286 kumpf 1.30 Message* ClientAuthenticator::releaseRequestMessage()
287 {
288 return _requestMessage.release();
|
289 mike 1.2 }
290
291 void ClientAuthenticator::setUserName(const String& userName)
292 {
293 _userName = userName;
294 }
295
296 String ClientAuthenticator::getUserName()
297 {
298 return (_userName);
299 }
300
301 void ClientAuthenticator::setPassword(const String& password)
302 {
303 _password = password;
304 }
305
306 void ClientAuthenticator::setAuthType(ClientAuthenticator::AuthType type)
307 {
|
308 kumpf 1.5 PEGASUS_ASSERT( (type == ClientAuthenticator::BASIC) ||
309 (type == ClientAuthenticator::DIGEST) ||
310 (type == ClientAuthenticator::LOCAL) ||
|
311 kumpf 1.9 (type == ClientAuthenticator::NONE) );
|
312 kumpf 1.5
|
313 mike 1.2 _authType = type;
314 }
315
316 ClientAuthenticator::AuthType ClientAuthenticator::getAuthType()
317 {
318 return (_authType);
319 }
320
|
321 kumpf 1.41 String ClientAuthenticator::_getFileContent(const String& filePath)
|
322 mike 1.2 {
|
323 kumpf 1.41 String translatedFilePath = filePath;
324 FileSystem::translateSlashes(translatedFilePath);
|
325 mike 1.2
326 //
|
327 kumpf 1.4 // Check whether the file exists or not
|
328 mike 1.2 //
|
329 kumpf 1.41 if (!FileSystem::exists(translatedFilePath))
|
330 kumpf 1.4 {
|
331 kumpf 1.41 throw NoSuchFile(translatedFilePath);
|
332 kumpf 1.4 }
|
333 mike 1.2
334 //
335 // Open the challenge file and read the challenge data
336 //
|
337 kumpf 1.41 ifstream ifs(translatedFilePath.getCString());
|
338 mike 1.2 if (!ifs)
339 {
|
340 kumpf 1.41 //ATTN: Log error message
341 return String::EMPTY;
|
342 mike 1.2 }
343
|
344 kumpf 1.41 String fileContent;
|
345 mike 1.2 String line;
346
347 while (GetLine(ifs, line))
348 {
|
349 kumpf 1.41 fileContent.append(line);
|
350 mike 1.2 }
351
352 ifs.close();
353
|
354 kumpf 1.41 return fileContent;
|
355 mike 1.2 }
356
|
357 kumpf 1.6 String ClientAuthenticator::_buildLocalAuthResponse()
358 {
|
359 kumpf 1.41 String authResponse;
|
360 kumpf 1.6
|
361 chip 1.7 if (_challengeReceived)
|
362 kumpf 1.6 {
363 authResponse.append(":");
364
365 //
366 // Append the file path that is in the realm sent by the server
367 //
|
368 kumpf 1.41 authResponse.append(_localAuthFile);
|
369 kumpf 1.6
370 authResponse.append(":");
371
|
372 kumpf 1.41 if (_localAuthFileContent.size() == 0)
|
373 kumpf 1.6 {
|
374 kumpf 1.41 //
375 // Read the challenge file content
376 //
377 try
378 {
379 _localAuthFileContent = _getFileContent(_localAuthFile);
380 }
381 catch (NoSuchFile&)
382 {
383 //ATTN-NB-04-20000305: Log error message to log file
384 }
|
385 kumpf 1.6 }
|
386 kumpf 1.41
387 authResponse.append(_localAuthFileContent);
|
388 kumpf 1.6 }
|
389 kumpf 1.41
|
390 kumpf 1.6 authResponse.append("\"");
391
|
392 kumpf 1.41 return authResponse;
|
393 kumpf 1.6 }
394
395 Boolean ClientAuthenticator::_parseAuthHeader(
|
396 kumpf 1.45 const char* authHeader,
|
397 chip 1.7 String& authType,
|
398 kumpf 1.6 String& authRealm)
399 {
400 //
401 // Skip the white spaces in the begining of the header
402 //
|
403 kumpf 1.45 while (*authHeader && isspace(*authHeader))
|
404 kumpf 1.6 {
|
405 kumpf 1.45 *authHeader++;
|
406 kumpf 1.6 }
407
408 //
409 // Get the authentication type
410 //
|
411 kumpf 1.45 String type = _getSubStringUptoMarker(&authHeader, CHAR_BLANK);
|
412 kumpf 1.6
413 if (!type.size())
414 {
415 return false;
416 }
417
418 //
419 // Ignore the start quote
420 //
|
421 kumpf 1.45 _getSubStringUptoMarker(&authHeader, CHAR_QUOTE);
|
422 kumpf 1.6
423
424 //
425 // Get the realm ending with a quote
426 //
|
427 kumpf 1.45 String realm = _getSubStringUptoMarker(&authHeader, CHAR_QUOTE);
|
428 kumpf 1.6
429 if (!realm.size())
430 {
431 return false;
432 }
433
434 authType = type;
435
436 authRealm = realm;
437
438 return true;
439 }
440
441
442 String ClientAuthenticator::_getSubStringUptoMarker(
|
443 chip 1.7 const char** line,
|
444 kumpf 1.6 char marker)
445 {
|
446 kumpf 1.38 String result;
|
447 kumpf 1.6
|
448 kumpf 1.38 if (*line)
449 {
450 //
451 // Look for the marker
452 //
453 const char *pos = strchr(*line, marker);
|
454 kumpf 1.6
|
455 kumpf 1.38 if (pos)
|
456 kumpf 1.6 {
|
457 kumpf 1.38 if (*line)
458 {
459 Uint32 length = (Uint32)(pos - *line);
460 result.assign(*line, length);
461 }
462
463 while (*pos == marker)
464 {
465 ++pos;
466 }
|
467 kumpf 1.6
|
468 kumpf 1.38 *line = pos;
|
469 kumpf 1.6 }
|
470 kumpf 1.38 else
471 {
472 result.assign(*line);
|
473 kumpf 1.6
|
474 kumpf 1.38 *line += strlen(*line);
|
475 kumpf 1.6 }
476 }
477
478 return result;
479 }
|
480 mike 1.2
481 PEGASUS_NAMESPACE_END
|