Added LDAP id to structure
[public/netxms.git] / src / server / core / userdb.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2016 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: userdb.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 /**
26 * Password complexity options
27 */
28 #define PSWD_MUST_CONTAIN_DIGITS 0x0001
29 #define PSWD_MUST_CONTAIN_UPPERCASE 0x0002
30 #define PSWD_MUST_CONTAIN_LOWERCASE 0x0004
31 #define PSWD_MUST_CONTAIN_SPECIAL_CHARS 0x0008
32 #define PSWD_FORBID_ALPHABETICAL_SEQUENCE 0x0010
33 #define PSWD_FORBID_KEYBOARD_SEQUENCE 0x0020
34
35 /**
36 * Action done on deleted user/group
37 */
38 #define USER_DELETE 0
39 #define USER_DISABLE 1
40
41 /**
42 * Externals
43 */
44 bool RadiusAuth(const TCHAR *pszLogin, const TCHAR *pszPasswd);
45
46 /**
47 * Static data
48 */
49 static HashMap<UINT32, UserDatabaseObject> s_userDatabase(true);
50 static StringObjectMap<UserDatabaseObject> s_ldapNames(false);
51 static StringObjectMap<User> s_ldapUserId(false);
52 static StringObjectMap<Group> s_ldapGroupId(false);
53 static StringObjectMap<User> s_users(false);
54 static StringObjectMap<Group> s_groups(false);
55 static RWLOCK s_userDatabaseLock = RWLockCreate();
56 static THREAD s_statusUpdateThread = INVALID_THREAD_HANDLE;
57
58 /**
59 * Compare user names
60 */
61 inline bool UserNameEquals(const TCHAR *n1, const TCHAR *n2)
62 {
63 return (g_flags & AF_CASE_INSENSITIVE_LOGINS) ? (_tcsicmp(n1, n2) == 0) : (_tcscmp(n1, n2) == 0);
64 }
65
66 /**
67 * Add user database object
68 */
69 inline void AddDatabaseObject(UserDatabaseObject *object)
70 {
71 s_userDatabase.set(object->getId(), object);
72 if (object->isGroup())
73 s_groups.set(object->getName(), (Group *)object);
74 else
75 s_users.set(object->getName(), (User *)object);
76 if (object->isLDAPUser())
77 {
78 s_ldapNames.set(object->getDn(), object);
79 if(object->getLdapId() != NULL)
80 {
81 if (object->isGroup())
82 s_ldapGroupId.set(object->getLdapId(), (Group *)object);
83 else
84 s_ldapUserId.set(object->getLdapId(), (User *)object);
85 }
86 }
87 }
88
89 /**
90 * Remove user database object
91 */
92 inline void RemoveDatabaseObject(UserDatabaseObject *object)
93 {
94 if (object->isGroup())
95 s_groups.remove(object->getName());
96 else
97 s_users.remove(object->getName());
98 if (object->isLDAPUser())
99 {
100 s_ldapNames.remove(object->getDn());
101 if(object->getLdapId() != NULL)
102 {
103 if (object->isGroup())
104 s_ldapGroupId.remove(object->getLdapId());
105 else
106 s_ldapUserId.remove(object->getLdapId());
107 }
108 }
109 }
110
111 /**
112 * Get effective system rights for user
113 */
114 static UINT64 GetEffectiveSystemRights(User *user)
115 {
116 UINT64 systemRights = user->getSystemRights();
117 Iterator<UserDatabaseObject> *it = s_userDatabase.iterator();
118 while(it->hasNext())
119 {
120 UserDatabaseObject *object = it->next();
121 if (object->isGroup() && (((Group *)object)->isMember(user->getId())))
122 systemRights |= ((Group *)object)->getSystemRights();
123 }
124 delete it;
125 return systemRights;
126 }
127
128 /**
129 * Upgrade user accounts status in background
130 */
131 static THREAD_RESULT THREAD_CALL AccountStatusUpdater(void *arg)
132 {
133 DbgPrintf(2, _T("User account status update thread started"));
134 while(!SleepAndCheckForShutdown(60))
135 {
136 DbgPrintf(8, _T("AccountStatusUpdater: wakeup"));
137
138 time_t blockInactiveAccounts = (time_t)ConfigReadInt(_T("BlockInactiveUserAccounts"), 0) * 86400;
139
140 RWLockWriteLock(s_userDatabaseLock, INFINITE);
141 time_t now = time(NULL);
142 Iterator<UserDatabaseObject> *it = s_userDatabase.iterator();
143 while(it->hasNext())
144 {
145 UserDatabaseObject *object = it->next();
146 if (object->isDeleted() || object->isGroup())
147 continue;
148
149 User *user = (User *)object;
150
151 if (user->isDisabled() && (user->getReEnableTime() > 0) && (user->getReEnableTime() <= now))
152 {
153 // Re-enable temporary disabled user
154 user->enable();
155 WriteAuditLog(AUDIT_SECURITY, TRUE, user->getId(), NULL, AUDIT_SYSTEM_SID, 0, _T("Temporary disabled user account \"%s\" re-enabled by system"), user->getName());
156 DbgPrintf(3, _T("Temporary disabled user account \"%s\" re-enabled"), user->getName());
157 }
158
159 if (!user->isDisabled() && (blockInactiveAccounts > 0) && (user->getLastLoginTime() > 0) && (user->getLastLoginTime() + blockInactiveAccounts < now))
160 {
161 user->disable();
162 WriteAuditLog(AUDIT_SECURITY, TRUE, user->getId(), NULL, AUDIT_SYSTEM_SID, 0, _T("User account \"%s\" disabled by system due to inactivity"), user->getName());
163 DbgPrintf(3, _T("User account \"%s\" disabled due to inactivity"), user->getName());
164 }
165 }
166 delete it;
167 RWLockUnlock(s_userDatabaseLock);
168 }
169
170 DbgPrintf(2, _T("User account status update thread stopped"));
171 return THREAD_OK;
172 }
173
174 /**
175 * Initialize user handling subsystem
176 */
177 void InitUsers()
178 {
179 s_users.setIgnoreCase((g_flags & AF_CASE_INSENSITIVE_LOGINS) != 0);
180 s_groups.setIgnoreCase((g_flags & AF_CASE_INSENSITIVE_LOGINS) != 0);
181 s_statusUpdateThread = ThreadCreateEx(AccountStatusUpdater, 0, NULL);
182 }
183
184 /**
185 * Cleanup user handling subsystem
186 */
187 void CleanupUsers()
188 {
189 ThreadJoin(s_statusUpdateThread);
190 }
191
192 /**
193 * Load user list from database
194 */
195 BOOL LoadUsers()
196 {
197 int i;
198 DB_RESULT hResult;
199
200 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
201
202 // Load users
203 hResult = DBSelect(hdb,
204 _T("SELECT id,name,system_access,flags,description,guid,ldap_dn,")
205 _T("ldap_unique_id,password,full_name,grace_logins,auth_method,")
206 _T("cert_mapping_method,cert_mapping_data,auth_failures,")
207 _T("last_passwd_change,min_passwd_length,disabled_until,")
208 _T("last_login,xmpp_id FROM users"));
209 if (hResult == NULL)
210 {
211 DBConnectionPoolReleaseConnection(hdb);
212 return false;
213 }
214
215 int count = DBGetNumRows(hResult);
216 for(i = 0; i < count; i++)
217 {
218 User *user = new User(hdb, hResult, i);
219 AddDatabaseObject(user);
220 }
221
222 DBFreeResult(hResult);
223
224 // Create superuser account if it doesn't exist
225 if (!s_userDatabase.contains(0))
226 {
227 User *user = new User();
228 AddDatabaseObject(user);
229 nxlog_write(MSG_SUPERUSER_CREATED, EVENTLOG_WARNING_TYPE, NULL);
230 }
231
232 // Load groups
233 hResult = DBSelect(hdb, _T("SELECT id,name,system_access,flags,description,guid,ldap_dn,ldap_unique_id FROM user_groups"));
234 if (hResult == NULL)
235 {
236 DBConnectionPoolReleaseConnection(hdb);
237 return FALSE;
238 }
239
240 count = DBGetNumRows(hResult);
241 for(i = 0; i < count; i++)
242 {
243 Group *group = new Group(hdb, hResult, i);
244 AddDatabaseObject(group);
245 }
246
247 DBFreeResult(hResult);
248
249 // Create everyone group if it doesn't exist
250 if (!s_userDatabase.contains(GROUP_EVERYONE))
251 {
252 Group *group = new Group();
253 AddDatabaseObject(group);
254 nxlog_write(MSG_EVERYONE_GROUP_CREATED, EVENTLOG_WARNING_TYPE, NULL);
255 }
256
257 DBConnectionPoolReleaseConnection(hdb);
258 return TRUE;
259 }
260
261 /**
262 * Save user list to database
263 */
264 void SaveUsers(DB_HANDLE hdb)
265 {
266 int i;
267
268 // Save users
269 RWLockWriteLock(s_userDatabaseLock, INFINITE);
270 Iterator<UserDatabaseObject> *it = s_userDatabase.iterator();
271 while(it->hasNext())
272 {
273 UserDatabaseObject *object = it->next();
274 if (object->isDeleted())
275 {
276 object->deleteFromDatabase(hdb);
277 RemoveDatabaseObject(object);
278 it->remove();
279 }
280 else if (object->isModified())
281 {
282 object->saveToDatabase(hdb);
283 }
284 }
285 delete it;
286 RWLockUnlock(s_userDatabaseLock);
287 }
288
289 /**
290 * Authenticate user
291 * Checks if provided login name and password are correct, and returns RCC_SUCCESS
292 * on success and appropriate RCC otherwise. On success authentication, user's ID is stored
293 * int pdwId. If password authentication is used, dwSigLen should be set to zero.
294 * For non-UNICODE build, password must be UTF-8 encoded. If user already authenticated by
295 * SSO server, ssoAuth must be set to true. Password expiration, change flag and grace
296 * count ignored for SSO logins.
297 */
298 UINT32 AuthenticateUser(const TCHAR *login, const TCHAR *password, UINT32 dwSigLen, void *pCert,
299 BYTE *pChallenge, UINT32 *pdwId, UINT64 *pdwSystemRights,
300 bool *pbChangePasswd, bool *pbIntruderLockout, bool ssoAuth)
301 {
302 int i, j;
303 UINT32 dwResult = RCC_ACCESS_DENIED;
304 BOOL bPasswordValid = FALSE;
305
306 RWLockWriteLock(s_userDatabaseLock, INFINITE);
307 User *user = s_users.get(login);
308 if ((user != NULL) && !user->isDeleted())
309 {
310 *pdwId = user->getId(); // always set user ID for caller so audit log will contain correct user ID on failures as well
311
312 if (user->isLDAPUser())
313 {
314 if (user->isDisabled() || user->hasSyncException())
315 {
316 dwResult = RCC_ACCOUNT_DISABLED;
317 goto result;
318 }
319 LDAPConnection conn;
320 dwResult = conn.ldapUserLogin(user->getDn(), password);
321 if (dwResult == RCC_SUCCESS)
322 bPasswordValid = TRUE;
323 goto result;
324 }
325
326 // Determine authentication method to use
327 if (!ssoAuth)
328 {
329 int method = user->getAuthMethod();
330 if ((method == AUTH_CERT_OR_PASSWD) || (method == AUTH_CERT_OR_RADIUS))
331 {
332 if (dwSigLen > 0)
333 {
334 // certificate auth
335 method = AUTH_CERTIFICATE;
336 }
337 else
338 {
339 method = (method == AUTH_CERT_OR_PASSWD) ? AUTH_NETXMS_PASSWORD : AUTH_RADIUS;
340 }
341 }
342
343 switch(method)
344 {
345 case AUTH_NETXMS_PASSWORD:
346 if (dwSigLen == 0)
347 {
348 bPasswordValid = user->validatePassword(password);
349 }
350 else
351 {
352 // We got certificate instead of password
353 bPasswordValid = FALSE;
354 }
355 break;
356 case AUTH_RADIUS:
357 if (dwSigLen == 0)
358 {
359 bPasswordValid = RadiusAuth(login, password);
360 }
361 else
362 {
363 // We got certificate instead of password
364 bPasswordValid = FALSE;
365 }
366 break;
367 case AUTH_CERTIFICATE:
368 if ((dwSigLen != 0) && (pCert != NULL))
369 {
370 #ifdef _WITH_ENCRYPTION
371 bPasswordValid = ValidateUserCertificate((X509 *)pCert, login, pChallenge,
372 (BYTE *)password, dwSigLen,
373 user->getCertMappingMethod(),
374 user->getCertMappingData());
375 #else
376 bPasswordValid = FALSE;
377 #endif
378 }
379 else
380 {
381 // We got password instead of certificate
382 bPasswordValid = FALSE;
383 }
384 break;
385 default:
386 nxlog_write(MSG_UNKNOWN_AUTH_METHOD, NXLOG_WARNING, "ds", user->getAuthMethod(), login);
387 bPasswordValid = FALSE;
388 break;
389 }
390 }
391 else
392 {
393 DbgPrintf(4, _T("User \"%s\" already authenticated by SSO server"), user->getName());
394 bPasswordValid = TRUE;
395 }
396
397 result:
398 if (bPasswordValid)
399 {
400 if (!user->isDisabled())
401 {
402 user->resetAuthFailures();
403 if (!ssoAuth)
404 {
405 if (user->getFlags() & UF_CHANGE_PASSWORD)
406 {
407 DbgPrintf(4, _T("Password for user \"%s\" need to be changed"), user->getName());
408 if (user->getId() != 0) // Do not check grace logins for built-in admin user
409 {
410 if (user->getGraceLogins() <= 0)
411 {
412 DbgPrintf(4, _T("User \"%s\" has no grace logins left"), user->getName());
413 dwResult = RCC_NO_GRACE_LOGINS;
414 }
415 else
416 {
417 user->decreaseGraceLogins();
418 }
419 }
420 *pbChangePasswd = true;
421 }
422 else
423 {
424 // Check if password was expired
425 int passwordExpirationTime = ConfigReadInt(_T("PasswordExpiration"), 0);
426 if ((user->getAuthMethod() == AUTH_NETXMS_PASSWORD) &&
427 (passwordExpirationTime > 0) &&
428 ((user->getFlags() & UF_PASSWORD_NEVER_EXPIRES) == 0) &&
429 (time(NULL) > user->getPasswordChangeTime() + passwordExpirationTime * 86400))
430 {
431 DbgPrintf(4, _T("Password for user \"%s\" has expired"), user->getName());
432 if (user->getId() != 0) // Do not check grace logins for built-in admin user
433 {
434 if (user->getGraceLogins() <= 0)
435 {
436 DbgPrintf(4, _T("User \"%s\" has no grace logins left"), user->getName());
437 dwResult = RCC_NO_GRACE_LOGINS;
438 }
439 else
440 {
441 user->decreaseGraceLogins();
442 }
443 }
444 *pbChangePasswd = true;
445 }
446 else
447 {
448 *pbChangePasswd = false;
449 }
450 }
451 }
452 else
453 {
454 *pbChangePasswd = false;
455 }
456
457 if (dwResult != RCC_NO_GRACE_LOGINS)
458 {
459 *pdwSystemRights = GetEffectiveSystemRights(user);
460 user->updateLastLogin();
461 dwResult = RCC_SUCCESS;
462 }
463 }
464 else
465 {
466 dwResult = RCC_ACCOUNT_DISABLED;
467 }
468 *pbIntruderLockout = false;
469 }
470 else
471 {
472 user->increaseAuthFailures();
473 *pbIntruderLockout = user->isIntruderLockoutActive();
474 }
475 }
476 RWLockUnlock(s_userDatabaseLock);
477 return dwResult;
478 }
479
480 /**
481 * Check if user is a member of specific group
482 */
483 bool NXCORE_EXPORTABLE CheckUserMembership(UINT32 userId, UINT32 groupId)
484 {
485 if (!(groupId & GROUP_FLAG))
486 return false;
487
488 if (groupId == GROUP_EVERYONE)
489 return true;
490
491 bool result = false;
492 RWLockReadLock(s_userDatabaseLock, INFINITE);
493 Group *group = (Group *)s_userDatabase.get(groupId);
494 if (group != NULL)
495 {
496 result = group->isMember(userId);
497 }
498 RWLockUnlock(s_userDatabaseLock);
499 return result;
500 }
501
502 /**
503 * Fill message with group membership information for given user.
504 * Access to user database must be locked.
505 */
506 void FillGroupMembershipInfo(NXCPMessage *msg, UINT32 userId)
507 {
508 IntegerArray<UINT32> list;
509 Iterator<UserDatabaseObject> *it = s_userDatabase.iterator();
510 while(it->hasNext())
511 {
512 UserDatabaseObject *object = it->next();
513 if (object->isGroup() && (object->getId() != GROUP_EVERYONE) && ((Group *)object)->isMember(userId))
514 {
515 list.add(object->getId());
516 }
517 }
518 delete it;
519 msg->setField(VID_NUM_GROUPS, (INT32)list.size());
520 if (list.size() > 0)
521 msg->setFieldFromInt32Array(VID_GROUPS, &list);
522 }
523
524 /**
525 * Update group membership for user
526 */
527 void UpdateGroupMembership(UINT32 userId, int numGroups, UINT32 *groups)
528 {
529 Iterator<UserDatabaseObject> *it = s_userDatabase.iterator();
530 while(it->hasNext())
531 {
532 UserDatabaseObject *object = it->next();
533 if (object->isGroup() && (object->getId() != GROUP_EVERYONE))
534 {
535 bool found = false;
536 for(int j = 0; j < numGroups; j++)
537 {
538 if (object->getId() == groups[j])
539 {
540 found = true;
541 break;
542 }
543 }
544 if (found)
545 {
546 ((Group *)object)->addUser(userId);
547 }
548 else
549 {
550 ((Group *)object)->deleteUser(userId);
551 }
552 }
553 }
554 delete it;
555 }
556
557 /**
558 * Resolve user's ID to login name
559 */
560 bool NXCORE_EXPORTABLE ResolveUserId(UINT32 id, TCHAR *buffer, int bufSize)
561 {
562 RWLockReadLock(s_userDatabaseLock, INFINITE);
563 UserDatabaseObject *object = s_userDatabase.get(id);
564 if (object != NULL)
565 nx_strncpy(buffer, object->getName(), bufSize);
566 RWLockUnlock(s_userDatabaseLock);
567 return object != NULL;
568 }
569
570 /**
571 * Check if provided user name is not used or belongs to given user.
572 * Access to user DB must be locked when this function is called
573 */
574 inline bool UserNameIsUnique(const TCHAR *name, User *user)
575 {
576 User *u = s_users.get(name);
577 return (u == NULL) || ((user != NULL) && (user->getId() == u->getId()));
578 }
579
580 /**
581 * Check if provided group name is not used or belongs to given group.
582 * Access to user DB must be locked when this function is called
583 */
584 inline bool GroupNameIsUnique(const TCHAR *name, Group *group)
585 {
586 Group *g = s_groups.get(name);
587 return (g == NULL) || ((group != NULL) && (group->getId() == g->getId()));
588 }
589
590 /**
591 * Generates unique name for LDAP user
592 */
593 static TCHAR *GenerateUniqueName(const TCHAR *oldName, UINT32 id)
594 {
595 TCHAR *name = (TCHAR *)malloc(sizeof(TCHAR) * 256);
596 _sntprintf(name, 256, _T("%s_LDAP%d"), oldName, id);
597 return name;
598 }
599
600 /**
601 * Update/Add LDAP user
602 */
603 void UpdateLDAPUser(const TCHAR *dn, Entry *obj)
604 {
605 RWLockWriteLock(s_userDatabaseLock, INFINITE);
606
607 bool userModified = false;
608 bool conflict = false;
609 TCHAR description[1024];
610 TCHAR guid[64];
611
612 // Check existing user with same DN
613 UserDatabaseObject *object = NULL;
614 if(obj->m_id != NULL)
615 object = s_ldapUserId.get(obj->m_id);
616 else
617 object = s_ldapNames.get(dn);
618
619 if ((object != NULL) && object->isGroup())
620 {
621 _sntprintf(description, MAX_USER_DESCR, _T("Got user with DN=%s but found existing group %s with same DN"), dn, object->getName());
622 object->getGuidAsText(guid);
623 PostEvent(EVENT_LDAP_SYCN_ERROR ,g_dwMgmtNode, "issss", object->getId(), guid, object->getDn(), object->getName(), description);
624 DbgPrintf(4, _T("UpdateLDAPUser(): %s"), description);
625 conflict = true;
626 }
627
628 if ((object != NULL) && !conflict)
629 {
630 User *user = (User *)object;
631 if (!user->isDeleted())
632 {
633 user->removeSyncException();
634 if (!UserNameIsUnique(obj->m_loginName, user))
635 {
636 TCHAR *userName = GenerateUniqueName(obj->m_loginName, user->getId());
637 if(_tcscmp(user->getName(), userName))
638 {
639 user->setSyncException();
640 TCHAR conflictDescription[MAX_USER_DESCR];
641 _sntprintf(conflictDescription, MAX_USER_DESCR, _T("User with name \"%s\" already exists."), obj->m_loginName);
642 user->setDescription(conflictDescription);
643 object->getGuidAsText(guid);
644 PostEvent(EVENT_LDAP_SYCN_ERROR ,g_dwMgmtNode, "issss", object->getId(), guid, object->getDn(), object->getName(), conflictDescription);
645 DbgPrintf(4, _T("UpdateLDAPUser(): %s"), conflictDescription);
646 }
647 free(userName);
648 }
649 else
650 {
651 user->setName(obj->m_loginName);
652 user->setFullName(obj->m_fullName);
653 user->setDescription(obj->m_description);
654 if(_tcscmp(user->getDn(), dn))
655 {
656 s_ldapNames.remove(user->getDn());
657 user->setDn(dn);
658 s_ldapNames.set(dn, user);
659 }
660 DbgPrintf(4, _T("UpdateLDAPUser(): User updated: ID: %s DN: %s, login name: %s, full name: %s, description: %s"), CHECK_NULL(obj->m_id), dn, obj->m_loginName, CHECK_NULL(obj->m_fullName), CHECK_NULL(obj->m_description));
661 }
662 if (user->isModified())
663 {
664 SendUserDBUpdate(USER_DB_MODIFY, user->getId(), user);
665 }
666 }
667 userModified = true;
668 }
669
670 if (!userModified && !conflict)
671 {
672 if (UserNameIsUnique(obj->m_loginName, NULL))
673 {
674 User *user = new User(CreateUniqueId(IDG_USER), obj->m_loginName);
675 user->setFullName(obj->m_fullName);
676 user->setDescription(obj->m_description);
677 user->setFlags(UF_MODIFIED | UF_LDAP_USER);
678 user->setDn(dn);
679 if(obj->m_id != NULL)
680 user->setLdapId(obj->m_id);
681 AddDatabaseObject(user);
682 SendUserDBUpdate(USER_DB_CREATE, user->getId(), user);
683 DbgPrintf(4, _T("UpdateLDAPUser(): User added: ID: %s DN: %s, login name: %s, full name: %s, description: %s"), CHECK_NULL(obj->m_id), dn, obj->m_loginName, CHECK_NULL(obj->m_fullName), CHECK_NULL(obj->m_description));
684 }
685 else
686 {
687 UINT32 userId = CreateUniqueId(IDG_USER);
688 TCHAR *userName = GenerateUniqueName(obj->m_loginName, userId);
689 _sntprintf(description, MAX_USER_DESCR, _T("User with name \"%s\" already exists. Unique user name have been generated: %s"), obj->m_loginName, userName);
690 DbgPrintf(4, _T("UpdateLDAPUser(): %s"), description);
691 User *user = new User(userId, userName);
692 user->setFullName(obj->m_fullName);
693 user->setDescription(obj->m_description);
694 user->setFlags(UF_MODIFIED | UF_LDAP_USER);
695 user->setDn(dn);
696 if(obj->m_id != NULL)
697 user->setLdapId(obj->m_id);
698 AddDatabaseObject(user);
699 SendUserDBUpdate(USER_DB_CREATE, user->getId(), user);
700 user->getGuidAsText(guid);
701 PostEvent(EVENT_LDAP_SYCN_ERROR ,g_dwMgmtNode, "issss", user->getId(), guid, user->getDn(), user->getName(), description);
702 DbgPrintf(4, _T("UpdateLDAPUser(): User added: ID: %s DN: %s, login name: %s, full name: %s, description: %s"), CHECK_NULL(obj->m_id), dn, userName, CHECK_NULL(obj->m_fullName), CHECK_NULL(obj->m_description));
703 free(userName);
704 }
705 }
706 RWLockUnlock(s_userDatabaseLock);
707 }
708
709 /**
710 * Goes through all existing LDAP entries and check that in newly gotten list they also exist.
711 * If LDAP entries does not exists in new list - it will be disabled or removed depending on action parameter.
712 */
713 void RemoveDeletedLDAPEntries(StringObjectMap<Entry> *entryListDn, StringObjectMap<Entry> *entryListId, UINT32 m_action, bool isUser)
714 {
715 RWLockWriteLock(s_userDatabaseLock, INFINITE);
716 Iterator<UserDatabaseObject> *it = s_userDatabase.iterator();
717 while(it->hasNext())
718 {
719 UserDatabaseObject *object = it->next();
720 if (!object->isLDAPUser() || object->isDeleted())
721 continue;
722
723 if (isUser ? ((object->getId() & GROUP_FLAG) == 0) : ((object->getId() & GROUP_FLAG) != 0))
724 {
725 if ((object->getLdapId() == NULL || !entryListId->contains(object->getLdapId())) && !entryListDn->contains(object->getDn()))
726 {
727 if (m_action == USER_DELETE)
728 {
729 DbgPrintf(4, _T("RemoveDeletedLDAPEntry(): LDAP %s object %s was removed from user database"), isUser ? _T("user") : _T("group"), object->getDn());
730 DeleteUserDatabaseObject(object->getId(), true);
731 }
732 else if (m_action == USER_DISABLE)
733 {
734 DbgPrintf(4, _T("RemoveDeletedLDAPEntry(): LDAP %s object %s was disabled"), isUser ? _T("user") : _T("group"), object->getDn());
735 object->disable();
736 object->setDescription(_T("LDAP entry was deleted."));
737 }
738 }
739 }
740 }
741 RWLockUnlock(s_userDatabaseLock);
742 }
743
744 /**
745 * Synchronize new user list with old user list of given group. Not LDAP usera will not be changed.
746 */
747 static void SyncGroupMembers(Group *group, Entry *obj)
748 {
749 DbgPrintf(4, _T("SyncGroupMembers(): Sync for LDAP group: %s"), group->getDn());
750
751 StringSet *newMembers = obj->m_memberList;
752 UINT32 *oldMembers = NULL;
753 int count = group->getMembers(&oldMembers);
754
755 /**
756 * Go through existing group member list checking each LDAP user by DN
757 * with new gotten group member list and removing LDAP users not existing in last list.
758 */
759 for(int i = 0; i < count; i++)
760 {
761 UserDatabaseObject *user = s_userDatabase.get(oldMembers[i]);
762 if ((user == NULL) || user->isGroup() || !user->isLDAPUser())
763 continue;
764
765 if (!newMembers->contains(user->getDn()))
766 {
767 DbgPrintf(4, _T("SyncGroupMembers: Remove from %s group deleted user: %s"), group->getDn(), user->getDn());
768 group->deleteUser(user->getId());
769 count = group->getMembers(&oldMembers);
770 i--;
771 }
772 }
773
774 /**
775 * Go through new gotten group member list checking each LDAP user by DN
776 * with existing group member list and adding users not existing in last list.
777 */
778 Iterator<const TCHAR> *it = newMembers->iterator();
779 while(it->hasNext())
780 {
781 const TCHAR *dn = it->next();
782 UserDatabaseObject *user = s_ldapNames.get(dn);
783 if ((user == NULL) || user->isGroup())
784 continue;
785
786 if (!group->isMember(user->getId()))
787 {
788 DbgPrintf(4, _T("SyncGroupMembers: LDAP user %s added to LDAP group %s"), user->getDn(), group->getDn());
789 group->addUser(user->getId());
790 }
791 }
792 delete it;
793 }
794
795 /**
796 * Update/Add LDAP group
797 */
798 void UpdateLDAPGroup(const TCHAR *dn, Entry *obj) //no full name, add users inside group, and delete removed from the group
799 {
800 RWLockWriteLock(s_userDatabaseLock, INFINITE);
801
802 bool userModified = false;
803 bool conflict = false;
804 TCHAR description[1024];
805 TCHAR guid[64];
806
807 // Check existing user with same DN
808 UserDatabaseObject *object = NULL;
809
810 if(obj->m_id != NULL)
811 object = s_ldapGroupId.get(obj->m_id);
812 else
813 object = s_ldapNames.get(dn);
814 if ((object != NULL) && !object->isGroup())
815 {
816 _sntprintf(description, MAX_USER_DESCR, _T("Got group with DN=%s but found existing group %s with same DN"), dn, object->getName());
817 object->getGuidAsText(guid);
818 PostEvent(EVENT_LDAP_SYCN_ERROR ,g_dwMgmtNode, "issss", object->getId(), guid, object->getDn(), object->getName(), description);
819 DbgPrintf(4, _T("UpdateLDAPGroup(): %s"), description);
820 conflict = true;
821 }
822
823 if ((object != NULL) && !conflict)
824 {
825 Group *group = (Group *)object;
826 if (!group->isDeleted())
827 {
828 group->removeSyncException();
829 if (!GroupNameIsUnique(obj->m_loginName, group))
830 {
831 TCHAR *groupName = GenerateUniqueName(obj->m_loginName, group->getId());
832 if(_tcscmp(group->getName(), groupName))
833 {
834 group->setSyncException();
835 TCHAR conflictDescription[MAX_USER_DESCR];
836 _sntprintf(conflictDescription, MAX_USER_DESCR, _T("Group with name \"%s\" already exists."), obj->m_loginName);
837 group->setDescription(conflictDescription);
838 object->getGuidAsText(guid);
839 PostEvent(EVENT_LDAP_SYCN_ERROR ,g_dwMgmtNode, "issss", object->getId(), guid, object->getDn(), object->getName(), conflictDescription);
840 DbgPrintf(4, _T("UpdateLDAPGroup(): %s"),conflictDescription);
841 }
842 free(groupName);
843 }
844 else
845 {
846 group->setName(obj->m_loginName);
847 group->setDescription(obj->m_description);
848 if(_tcscmp(group->getDn(), dn))
849 {
850 s_ldapNames.remove(group->getDn());
851 group->setDn(dn);
852 s_ldapNames.set(dn, group);
853 }
854 DbgPrintf(4, _T("UpdateLDAPGroup(): Group updated: ID: %s DN: %s, login name: %s, description: %s"), CHECK_NULL(obj->m_id), dn, obj->m_loginName, CHECK_NULL(obj->m_description));
855 }
856 if (group->isModified())
857 {
858 SendUserDBUpdate(USER_DB_MODIFY, group->getId(), group);
859 }
860 SyncGroupMembers(group , obj);
861 }
862 userModified = true;
863 }
864
865 if (!userModified)
866 {
867 if (GroupNameIsUnique(obj->m_loginName, NULL))
868 {
869 Group *group = new Group(CreateUniqueId(IDG_USER_GROUP), obj->m_loginName);
870 group->setDescription(obj->m_description);
871 group->setFlags(UF_MODIFIED | UF_LDAP_USER);
872 group->setDn(dn);
873 if(obj->m_id != NULL)
874 group->setLdapId(obj->m_id);
875 SendUserDBUpdate(USER_DB_CREATE, group->getId(), group);
876 AddDatabaseObject(group);
877 SyncGroupMembers(group , obj);
878 DbgPrintf(4, _T("UpdateLDAPGroup(): Group added: ID: %s DN: %s, login name: %s, description: %s"), CHECK_NULL(obj->m_id), dn, obj->m_loginName, CHECK_NULL(obj->m_description));
879 }
880 else
881 {
882 UINT32 id = CreateUniqueId(IDG_USER_GROUP);
883 TCHAR *groupName = GenerateUniqueName(obj->m_loginName, id);
884 _sntprintf(description, MAX_USER_DESCR, _T("Group with name \"%s\" already exists. Unique group name have been generated: %s"), obj->m_loginName, groupName);
885 DbgPrintf(4, _T("UpdateLDAPGroup(): %s"),description);
886 Group *group = new Group(id, groupName);
887 group->setDescription(obj->m_description);
888 group->setFlags(UF_MODIFIED | UF_LDAP_USER);
889 group->setDn(dn);
890 if(obj->m_id != NULL)
891 group->setLdapId(obj->m_id);
892 SendUserDBUpdate(USER_DB_CREATE, group->getId(), group);
893 AddDatabaseObject(group);
894 SyncGroupMembers(group , obj);
895 group->getGuidAsText(guid);
896 PostEvent(EVENT_LDAP_SYCN_ERROR ,g_dwMgmtNode, "issss", group->getId(), guid, group->getDn(), group->getName(), description);
897 DbgPrintf(4, _T("UpdateLDAPGroup(): Group added: ID: %s DN: %s, login name: %s, description: %s"), CHECK_NULL(obj->m_id), dn, obj->m_loginName, CHECK_NULL(obj->m_description));
898 free(groupName);
899 }
900 }
901 RWLockUnlock(s_userDatabaseLock);
902 }
903
904 /**
905 * Dump user list to console
906 *
907 * @param pCtx console context
908 */
909 void DumpUsers(CONSOLE_CTX pCtx)
910 {
911 ConsolePrintf(pCtx, _T("Login name GUID System rights\n")
912 _T("-----------------------------------------------------------------------\n"));
913
914 RWLockReadLock(s_userDatabaseLock, INFINITE);
915 Iterator<UserDatabaseObject> *it = s_userDatabase.iterator();
916 while(it->hasNext())
917 {
918 UserDatabaseObject *object = it->next();
919 if (!object->isGroup())
920 {
921 TCHAR szGUID[64];
922 UINT64 systemRights = GetEffectiveSystemRights((User *)object);
923 ConsolePrintf(pCtx, _T("%-20s %-36s 0x") UINT64X_FMT(_T("016")) _T("\n"), object->getName(), object->getGuidAsText(szGUID), systemRights);
924 }
925 }
926 delete it;
927 RWLockUnlock(s_userDatabaseLock);
928 ConsolePrintf(pCtx, _T("\n"));
929 }
930
931 /**
932 * Delete user or group
933 *
934 * @param id user database object ID
935 * @return RCC ready to be sent to client
936 */
937 UINT32 NXCORE_EXPORTABLE DeleteUserDatabaseObject(UINT32 id, bool alreadyLocked)
938 {
939 int i, j;
940
941 DeleteUserFromAllObjects(id);
942
943 if (!alreadyLocked)
944 RWLockWriteLock(s_userDatabaseLock, INFINITE);
945
946 UserDatabaseObject *object = s_userDatabase.get(id);
947 if (object != NULL)
948 {
949 object->setDeleted();
950 if (!(id & GROUP_FLAG))
951 {
952 Iterator<UserDatabaseObject> *it = s_userDatabase.iterator();
953 while(it->hasNext())
954 {
955 UserDatabaseObject *group = it->next();
956 if (group->getId() & GROUP_FLAG)
957 {
958 ((Group *)group)->deleteUser(id);
959 }
960 }
961 delete it;
962 }
963 }
964
965 if (!alreadyLocked)
966 RWLockUnlock(s_userDatabaseLock);
967
968 SendUserDBUpdate(USER_DB_DELETE, id, NULL);
969 return RCC_SUCCESS;
970 }
971
972 /**
973 * Create new user or group
974 */
975 UINT32 NXCORE_EXPORTABLE CreateNewUser(const TCHAR *name, bool isGroup, UINT32 *id)
976 {
977 UINT32 dwResult = RCC_SUCCESS;
978
979 RWLockWriteLock(s_userDatabaseLock, INFINITE);
980
981 // Check for duplicate name
982 UserDatabaseObject *object = isGroup ? (UserDatabaseObject *)s_groups.get(name) : (UserDatabaseObject *)s_users.get(name);
983 if (object != NULL)
984 {
985 dwResult = RCC_OBJECT_ALREADY_EXISTS;
986 }
987
988 if (dwResult == RCC_SUCCESS)
989 {
990 if (isGroup)
991 {
992 object = new Group(CreateUniqueId(IDG_USER_GROUP), name);
993 }
994 else
995 {
996 object = new User(CreateUniqueId(IDG_USER), name);
997 }
998 AddDatabaseObject(object);
999 SendUserDBUpdate(USER_DB_CREATE, object->getId(), object);
1000 *id = object->getId();
1001 }
1002
1003 RWLockUnlock(s_userDatabaseLock);
1004 return dwResult;
1005 }
1006
1007 /**
1008 * Modify user database object
1009 */
1010 UINT32 NXCORE_EXPORTABLE ModifyUserDatabaseObject(NXCPMessage *msg)
1011 {
1012 UINT32 id, fields, dwResult = RCC_INVALID_USER_ID;
1013 int i;
1014
1015 id = msg->getFieldAsUInt32(VID_USER_ID);
1016
1017 RWLockWriteLock(s_userDatabaseLock, INFINITE);
1018
1019 UserDatabaseObject *object = s_userDatabase.get(id);
1020 if (object != NULL)
1021 {
1022 TCHAR name[MAX_USER_NAME];
1023
1024 fields = msg->getFieldAsUInt32(VID_FIELDS);
1025 if (fields & USER_MODIFY_LOGIN_NAME)
1026 {
1027 msg->getFieldAsString(VID_USER_NAME, name, MAX_USER_NAME);
1028 if (!IsValidObjectName(name))
1029 {
1030 dwResult = RCC_INVALID_OBJECT_NAME;
1031 }
1032 }
1033
1034 if (dwResult != RCC_INVALID_OBJECT_NAME)
1035 {
1036 object->modifyFromMessage(msg);
1037 SendUserDBUpdate(USER_DB_MODIFY, id, object);
1038 dwResult = RCC_SUCCESS;
1039 }
1040 }
1041
1042 RWLockUnlock(s_userDatabaseLock);
1043 return dwResult;
1044 }
1045
1046 /**
1047 * Modify user database object
1048 */
1049 UINT32 NXCORE_EXPORTABLE DetachLdapUser(UINT32 id)
1050 {
1051 UINT32 dwResult = RCC_INVALID_USER_ID;
1052
1053 RWLockWriteLock(s_userDatabaseLock, INFINITE);
1054
1055 UserDatabaseObject *object = s_userDatabase.get(id);
1056 if (object != NULL)
1057 {
1058 s_ldapNames.remove(object->getDn());
1059 object->detachLdapUser();
1060 SendUserDBUpdate(USER_DB_MODIFY, id, object);
1061 dwResult = RCC_SUCCESS;
1062 }
1063
1064 RWLockUnlock(s_userDatabaseLock);
1065 return dwResult;
1066 }
1067
1068 /**
1069 * Send user DB update for given user ID.
1070 * Access to user database must be already locked.
1071 */
1072 void SendUserDBUpdate(int code, UINT32 id)
1073 {
1074 UserDatabaseObject *object = s_userDatabase.get(id);
1075 if (object != NULL)
1076 {
1077 SendUserDBUpdate(code, id, object);
1078 }
1079 }
1080
1081 /**
1082 * Check if string contains subsequence of given sequence
1083 */
1084 static bool IsStringContainsSubsequence(const TCHAR *str, const TCHAR *sequence, int len)
1085 {
1086 int sequenceLen = (int)_tcslen(sequence);
1087 if ((sequenceLen < len) || (len > 255))
1088 return false;
1089
1090 TCHAR subseq[256];
1091 for(int i = 0; i < sequenceLen - len; i++)
1092 {
1093 nx_strncpy(subseq, &sequence[i], len + 1);
1094 if (_tcsstr(str, subseq) != NULL)
1095 return true;
1096 }
1097
1098 return false;
1099 }
1100
1101 /**
1102 * Check password's complexity
1103 */
1104 static bool CheckPasswordComplexity(const TCHAR *password)
1105 {
1106 int flags = ConfigReadInt(_T("PasswordComplexity"), 0);
1107
1108 if ((flags & PSWD_MUST_CONTAIN_DIGITS) && (_tcspbrk(password, _T("0123456789")) == NULL))
1109 return false;
1110
1111 if ((flags & PSWD_MUST_CONTAIN_UPPERCASE) && (_tcspbrk(password, _T("ABCDEFGHIJKLMNOPQRSTUVWXYZ")) == NULL))
1112 return false;
1113
1114 if ((flags & PSWD_MUST_CONTAIN_LOWERCASE) && (_tcspbrk(password, _T("abcdefghijklmnopqrstuvwxyz")) == NULL))
1115 return false;
1116
1117 if ((flags & PSWD_MUST_CONTAIN_SPECIAL_CHARS) && (_tcspbrk(password, _T("`~!@#$%^&*()_-=+{}[]|\\'\";:,.<>/?")) == NULL))
1118 return false;
1119
1120 if (flags & PSWD_FORBID_ALPHABETICAL_SEQUENCE)
1121 {
1122 if (IsStringContainsSubsequence(password, _T("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), 3))
1123 return false;
1124 if (IsStringContainsSubsequence(password, _T("abcdefghijklmnopqrstuvwxyz"), 3))
1125 return false;
1126 }
1127
1128 if (flags & PSWD_FORBID_KEYBOARD_SEQUENCE)
1129 {
1130 if (IsStringContainsSubsequence(password, _T("~!@#$%^&*()_+"), 3))
1131 return false;
1132 if (IsStringContainsSubsequence(password, _T("1234567890-="), 3))
1133 return false;
1134 if (IsStringContainsSubsequence(password, _T("qwertyuiop[]"), 3))
1135 return false;
1136 if (IsStringContainsSubsequence(password, _T("asdfghjkl;'"), 3))
1137 return false;
1138 if (IsStringContainsSubsequence(password, _T("zxcvbnm,./"), 3))
1139 return false;
1140 if (IsStringContainsSubsequence(password, _T("QWERTYUIOP{}"), 3))
1141 return false;
1142 if (IsStringContainsSubsequence(password, _T("ASDFGHJKL:\""), 3))
1143 return false;
1144 if (IsStringContainsSubsequence(password, _T("ZXCVBNM<>?"), 3))
1145 return false;
1146 }
1147
1148 return true;
1149 }
1150
1151 /**
1152 * Set user's password
1153 * For non-UNICODE build, passwords must be UTF-8 encoded
1154 */
1155 UINT32 NXCORE_EXPORTABLE SetUserPassword(UINT32 id, const TCHAR *newPassword, const TCHAR *oldPassword, bool changeOwnPassword)
1156 {
1157 if (id & GROUP_FLAG)
1158 return RCC_INVALID_USER_ID;
1159
1160 RWLockWriteLock(s_userDatabaseLock, INFINITE);
1161
1162 // Find user
1163 User *user = (User *)s_userDatabase.get(id);
1164 if (user == NULL)
1165 {
1166 RWLockUnlock(s_userDatabaseLock);
1167 return RCC_INVALID_USER_ID;
1168 }
1169
1170 UINT32 dwResult = RCC_INVALID_USER_ID;
1171 if (changeOwnPassword)
1172 {
1173 if (!user->canChangePassword() || !user->validatePassword(oldPassword))
1174 {
1175 dwResult = RCC_ACCESS_DENIED;
1176 goto finish;
1177 }
1178
1179 // Check password length
1180 int minLength = (user->getMinMasswordLength() == -1) ? ConfigReadInt(_T("MinPasswordLength"), 0) : user->getMinMasswordLength();
1181 if ((int)_tcslen(newPassword) < minLength)
1182 {
1183 dwResult = RCC_WEAK_PASSWORD;
1184 goto finish;
1185 }
1186
1187 // Check password complexity
1188 if (!CheckPasswordComplexity(newPassword))
1189 {
1190 dwResult = RCC_WEAK_PASSWORD;
1191 goto finish;
1192 }
1193
1194 // Update password history
1195 int passwordHistoryLength = ConfigReadInt(_T("PasswordHistoryLength"), 0);
1196 if (passwordHistoryLength > 0)
1197 {
1198 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1199
1200 TCHAR query[8192], *ph = NULL;
1201
1202 _sntprintf(query, 8192, _T("SELECT password_history FROM users WHERE id=%d"), id);
1203 DB_RESULT hResult = DBSelect(hdb, query);
1204 if (hResult != NULL)
1205 {
1206 if (DBGetNumRows(hResult) > 0)
1207 {
1208 ph = DBGetField(hResult, 0, 0, NULL, 0);
1209 }
1210 DBFreeResult(hResult);
1211 }
1212
1213 if (ph != NULL)
1214 {
1215 BYTE newPasswdHash[SHA1_DIGEST_SIZE];
1216 #ifdef UNICODE
1217 char *mb = UTF8StringFromWideString(newPassword);
1218 CalculateSHA1Hash((BYTE *)mb, strlen(mb), newPasswdHash);
1219 free(mb);
1220 #else
1221 CalculateSHA1Hash((BYTE *)newPassword, strlen(newPassword), newPasswdHash);
1222 #endif
1223
1224 int phLen = (int)_tcslen(ph) / (SHA1_DIGEST_SIZE * 2);
1225 if (phLen > passwordHistoryLength)
1226 phLen = passwordHistoryLength;
1227
1228 for(int i = 0; i < phLen; i++)
1229 {
1230 BYTE hash[SHA1_DIGEST_SIZE];
1231 StrToBin(&ph[i * SHA1_DIGEST_SIZE * 2], hash, SHA1_DIGEST_SIZE);
1232 if (!memcmp(hash, newPasswdHash, SHA1_DIGEST_SIZE))
1233 {
1234 dwResult = RCC_REUSED_PASSWORD;
1235 goto finish;
1236 }
1237 }
1238
1239 if (dwResult != RCC_REUSED_PASSWORD)
1240 {
1241 if (phLen == passwordHistoryLength)
1242 {
1243 memmove(ph, &ph[SHA1_DIGEST_SIZE * 2], (phLen - 1) * SHA1_DIGEST_SIZE * 2 * sizeof(TCHAR));
1244 }
1245 else
1246 {
1247 ph = (TCHAR *)realloc(ph, (phLen + 1) * SHA1_DIGEST_SIZE * 2 * sizeof(TCHAR) + sizeof(TCHAR));
1248 phLen++;
1249 }
1250 BinToStr(newPasswdHash, SHA1_DIGEST_SIZE, &ph[(phLen - 1) * SHA1_DIGEST_SIZE * 2]);
1251
1252 _sntprintf(query, 8192, _T("UPDATE users SET password_history='%s' WHERE id=%d"), ph, id);
1253 DBQuery(hdb, query);
1254 }
1255
1256 free(ph);
1257 if (dwResult == RCC_REUSED_PASSWORD)
1258 goto finish;
1259 }
1260 else
1261 {
1262 dwResult = RCC_DB_FAILURE;
1263 goto finish;
1264 }
1265 DBConnectionPoolReleaseConnection(hdb);
1266 }
1267
1268 user->updatePasswordChangeTime();
1269 }
1270 user->setPassword(newPassword, changeOwnPassword);
1271 dwResult = RCC_SUCCESS;
1272
1273 finish:
1274 RWLockUnlock(s_userDatabaseLock);
1275 return dwResult;
1276 }
1277
1278 /**
1279 * Validate user's password
1280 */
1281 UINT32 NXCORE_EXPORTABLE ValidateUserPassword(UINT32 userId, const TCHAR *login, const TCHAR *password, bool *isValid)
1282 {
1283 if (userId & GROUP_FLAG)
1284 return RCC_INVALID_USER_ID;
1285
1286 UINT32 rcc = RCC_INVALID_USER_ID;
1287 RWLockReadLock(s_userDatabaseLock, INFINITE);
1288
1289 // Find user
1290 User *user = (User *)s_userDatabase.get(userId);
1291 if (user != NULL)
1292 {
1293 rcc = RCC_SUCCESS;
1294 if (user->isLDAPUser())
1295 {
1296 if (user->isDisabled() || user->hasSyncException())
1297 {
1298 rcc = RCC_ACCOUNT_DISABLED;
1299 }
1300 else
1301 {
1302 LDAPConnection conn;
1303 rcc = conn.ldapUserLogin(user->getDn(), password);
1304 if (rcc == RCC_SUCCESS)
1305 {
1306 *isValid = true;
1307 }
1308 else if (rcc == RCC_ACCESS_DENIED)
1309 {
1310 *isValid = false;
1311 rcc = RCC_SUCCESS;
1312 }
1313 }
1314 }
1315 else
1316 {
1317 switch(user->getAuthMethod())
1318 {
1319 case AUTH_NETXMS_PASSWORD:
1320 case AUTH_CERT_OR_PASSWD:
1321 *isValid = user->validatePassword(password);
1322 break;
1323 case AUTH_RADIUS:
1324 case AUTH_CERT_OR_RADIUS:
1325 *isValid = RadiusAuth(login, password);
1326 break;
1327 default:
1328 rcc = RCC_UNSUPPORTED_AUTH_METHOD;
1329 break;
1330 }
1331 }
1332 }
1333
1334 RWLockUnlock(s_userDatabaseLock);
1335 return rcc;
1336 }
1337
1338 /**
1339 * Open user database
1340 */
1341 Iterator<UserDatabaseObject> NXCORE_EXPORTABLE *OpenUserDatabase()
1342 {
1343 RWLockReadLock(s_userDatabaseLock, INFINITE);
1344 return s_userDatabase.iterator();
1345 }
1346
1347 /**
1348 * Close user database
1349 */
1350 void NXCORE_EXPORTABLE CloseUserDatabase(Iterator<UserDatabaseObject> *it)
1351 {
1352 delete it;
1353 RWLockUnlock(s_userDatabaseLock);
1354 }
1355
1356 /**
1357 * Get custom attribute's value
1358 */
1359 const TCHAR NXCORE_EXPORTABLE *GetUserDbObjectAttr(UINT32 id, const TCHAR *name)
1360 {
1361 const TCHAR *value = NULL;
1362
1363 RWLockReadLock(s_userDatabaseLock, INFINITE);
1364
1365 UserDatabaseObject *object = s_userDatabase.get(id);
1366 if (object != NULL)
1367 value = object->getAttribute(name);
1368
1369 RWLockUnlock(s_userDatabaseLock);
1370 return value;
1371 }
1372
1373 /**
1374 * Get custom attribute's value as unsigned integer
1375 */
1376 UINT32 NXCORE_EXPORTABLE GetUserDbObjectAttrAsULong(UINT32 id, const TCHAR *name)
1377 {
1378 const TCHAR *value = GetUserDbObjectAttr(id, name);
1379 return (value != NULL) ? _tcstoul(value, NULL, 0) : 0;
1380 }
1381
1382 /**
1383 * Set custom attribute's value
1384 */
1385 void NXCORE_EXPORTABLE SetUserDbObjectAttr(UINT32 id, const TCHAR *name, const TCHAR *value)
1386 {
1387 RWLockWriteLock(s_userDatabaseLock, INFINITE);
1388
1389 UserDatabaseObject *object = s_userDatabase.get(id);
1390 if (object != NULL)
1391 {
1392 object->setAttribute(name, value);
1393 }
1394
1395 RWLockUnlock(s_userDatabaseLock);
1396 }
1397
1398 /**
1399 * Authenticate user for XMPP subscription
1400 */
1401 bool AuthenticateUserForXMPPSubscription(const char *xmppId)
1402 {
1403 if (*xmppId == 0)
1404 return false;
1405
1406 bool success = false;
1407
1408 #ifdef UNICODE
1409 WCHAR *_xmppId = WideStringFromUTF8String(xmppId);
1410 #else
1411 char *_xmppId = strdup(xmppId);
1412 #endif
1413
1414 // Remove possible resource at the end
1415 TCHAR *sep = _tcschr(_xmppId, _T('/'));
1416 if (sep != NULL)
1417 *sep = 0;
1418
1419 RWLockReadLock(s_userDatabaseLock, INFINITE);
1420 Iterator<UserDatabaseObject> *it = s_userDatabase.iterator();
1421 while(it->hasNext())
1422 {
1423 UserDatabaseObject *object = it->next();
1424 if (!object->isGroup() &&
1425 !object->isDisabled() && !object->isDeleted() &&
1426 !_tcsicmp(_xmppId, ((User *)object)->getXmppId()))
1427 {
1428 DbgPrintf(4, _T("User %s authenticated for XMPP subscription"), object->getName());
1429
1430 TCHAR workstation[256];
1431 _tcscpy(workstation, _T("XMPP:"));
1432 nx_strncpy(&workstation[5], _xmppId, 251);
1433 WriteAuditLog(AUDIT_SECURITY, TRUE, object->getId(), workstation, AUDIT_SYSTEM_SID, 0, _T("User authenticated for XMPP subscription"));
1434
1435 success = true;
1436 break;
1437 }
1438 }
1439 delete it;
1440 RWLockUnlock(s_userDatabaseLock);
1441
1442 free(_xmppId);
1443 return success;
1444 }
1445
1446 /**
1447 * Authenticate user for XMPP commands
1448 */
1449 bool AuthenticateUserForXMPPCommands(const char *xmppId)
1450 {
1451 if (*xmppId == 0)
1452 return false;
1453
1454 bool success = false;
1455
1456 #ifdef UNICODE
1457 WCHAR *_xmppId = WideStringFromUTF8String(xmppId);
1458 #else
1459 char *_xmppId = strdup(xmppId);
1460 #endif
1461
1462 // Remove possible resource at the end
1463 TCHAR *sep = _tcschr(_xmppId, _T('/'));
1464 if (sep != NULL)
1465 *sep = 0;
1466
1467 RWLockReadLock(s_userDatabaseLock, INFINITE);
1468 Iterator<UserDatabaseObject> *it = s_userDatabase.iterator();
1469 while(it->hasNext())
1470 {
1471 UserDatabaseObject *object = it->next();
1472 if (!object->isGroup() &&
1473 !object->isDisabled() && !object->isDeleted() &&
1474 !_tcsicmp(_xmppId, ((User *)object)->getXmppId()))
1475 {
1476 UINT64 systemRights = GetEffectiveSystemRights((User *)object);
1477
1478 TCHAR workstation[256];
1479 _tcscpy(workstation, _T("XMPP:"));
1480 nx_strncpy(&workstation[5], _xmppId, 251);
1481
1482 if (systemRights & SYSTEM_ACCESS_XMPP_COMMANDS)
1483 {
1484 DbgPrintf(4, _T("User %s authenticated for XMPP commands"), object->getName());
1485 WriteAuditLog(AUDIT_SECURITY, TRUE, object->getId(), workstation, AUDIT_SYSTEM_SID, 0, _T("User authenticated for XMPP commands"));
1486 success = true;
1487 }
1488 else
1489 {
1490 DbgPrintf(4, _T("Access to XMPP commands denied for user %s"), object->getName());
1491 WriteAuditLog(AUDIT_SECURITY, FALSE, object->getId(), workstation, AUDIT_SYSTEM_SID, 0, _T("Access to XMPP commands denied"));
1492 }
1493 break;
1494 }
1495 }
1496 delete it;
1497 RWLockUnlock(s_userDatabaseLock);
1498
1499 free(_xmppId);
1500 return success;
1501 }