1 // SoftEther VPN Source Code - Stable Edition Repository
2 // Cedar Communication Module
3 //
4 // SoftEther VPN Server, Client and Bridge are free software under the Apache License, Version 2.0.
5 //
6 // Copyright (c) Daiyuu Nobori.
7 // Copyright (c) SoftEther VPN Project, University of Tsukuba, Japan.
8 // Copyright (c) SoftEther Corporation.
9 // Copyright (c) all contributors on SoftEther VPN project in GitHub.
10 //
11 // All Rights Reserved.
12 //
13 // http://www.softether.org/
14 //
15 // This stable branch is officially managed by Daiyuu Nobori, the owner of SoftEther VPN Project.
16 // Pull requests should be sent to the Developer Edition Master Repository on https://github.com/SoftEtherVPN/SoftEtherVPN
17 //
18 // License: The Apache License, Version 2.0
19 // https://www.apache.org/licenses/LICENSE-2.0
20 //
21 // DISCLAIMER
22 // ==========
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 // SOFTWARE.
31 //
32 // THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN, UNDER
33 // JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY, MERGE, PUBLISH,
34 // DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS SOFTWARE, THAT ANY
35 // JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS SOFTWARE OR ITS CONTENTS,
36 // AGAINST US (SOFTETHER PROJECT, SOFTETHER CORPORATION, DAIYUU NOBORI OR OTHER
37 // SUPPLIERS), OR ANY JURIDICAL DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND
38 // OF USING, COPYING, MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING,
39 // AND/OR SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
40 // CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO EXCLUSIVE
41 // JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO, JAPAN. YOU MUST WAIVE
42 // ALL DEFENSES OF LACK OF PERSONAL JURISDICTION AND FORUM NON CONVENIENS.
43 // PROCESS MAY BE SERVED ON EITHER PARTY IN THE MANNER AUTHORIZED BY APPLICABLE
44 // LAW OR COURT RULE.
45 //
46 // USE ONLY IN JAPAN. DO NOT USE THIS SOFTWARE IN ANOTHER COUNTRY UNLESS YOU HAVE
47 // A CONFIRMATION THAT THIS SOFTWARE DOES NOT VIOLATE ANY CRIMINAL LAWS OR CIVIL
48 // RIGHTS IN THAT PARTICULAR COUNTRY. USING THIS SOFTWARE IN OTHER COUNTRIES IS
49 // COMPLETELY AT YOUR OWN RISK. THE SOFTETHER VPN PROJECT HAS DEVELOPED AND
50 // DISTRIBUTED THIS SOFTWARE TO COMPLY ONLY WITH THE JAPANESE LAWS AND EXISTING
51 // CIVIL RIGHTS INCLUDING PATENTS WHICH ARE SUBJECTS APPLY IN JAPAN. OTHER
52 // COUNTRIES' LAWS OR CIVIL RIGHTS ARE NONE OF OUR CONCERNS NOR RESPONSIBILITIES.
53 // WE HAVE NEVER INVESTIGATED ANY CRIMINAL REGULATIONS, CIVIL LAWS OR
54 // INTELLECTUAL PROPERTY RIGHTS INCLUDING PATENTS IN ANY OF OTHER 200+ COUNTRIES
55 // AND TERRITORIES. BY NATURE, THERE ARE 200+ REGIONS IN THE WORLD, WITH
56 // DIFFERENT LAWS. IT IS IMPOSSIBLE TO VERIFY EVERY COUNTRIES' LAWS, REGULATIONS
57 // AND CIVIL RIGHTS TO MAKE THE SOFTWARE COMPLY WITH ALL COUNTRIES' LAWS BY THE
58 // PROJECT. EVEN IF YOU WILL BE SUED BY A PRIVATE ENTITY OR BE DAMAGED BY A
59 // PUBLIC SERVANT IN YOUR COUNTRY, THE DEVELOPERS OF THIS SOFTWARE WILL NEVER BE
60 // LIABLE TO RECOVER OR COMPENSATE SUCH DAMAGES, CRIMINAL OR CIVIL
61 // RESPONSIBILITIES. NOTE THAT THIS LINE IS NOT LICENSE RESTRICTION BUT JUST A
62 // STATEMENT FOR WARNING AND DISCLAIMER.
63 //
64 // READ AND UNDERSTAND THE 'WARNING.TXT' FILE BEFORE USING THIS SOFTWARE.
65 // SOME SOFTWARE PROGRAMS FROM THIRD PARTIES ARE INCLUDED ON THIS SOFTWARE WITH
66 // LICENSE CONDITIONS WHICH ARE DESCRIBED ON THE 'THIRD_PARTY.TXT' FILE.
67 //
68 //
69 // SOURCE CODE CONTRIBUTION
70 // ------------------------
71 //
72 // Your contribution to SoftEther VPN Project is much appreciated.
73 // Please send patches to us through GitHub.
74 // Read the SoftEther VPN Patch Acceptance Policy in advance:
75 // http://www.softether.org/5-download/src/9.patch
76 //
77 //
78 // DEAR SECURITY EXPERTS
79 // ---------------------
80 //
81 // If you find a bug or a security vulnerability please kindly inform us
82 // about the problem immediately so that we can fix the security problem
83 // to protect a lot of users around the world as soon as possible.
84 //
85 // Our e-mail address for security reports is:
86 // softether-vpn-security [at] softether.org
87 //
88 // Please note that the above e-mail address is not a technical support
89 // inquiry address. If you need technical assistance, please visit
90 // http://www.softether.org/ and ask your question on the users forum.
91 //
92 // Thank you for your cooperation.
93 //
94 //
95 // NO MEMORY OR RESOURCE LEAKS
96 // ---------------------------
97 //
98 // The memory-leaks and resource-leaks verification under the stress
99 // test has been passed before release this source code.
100 
101 
102 // Sam.c
103 // Security Accounts Manager
104 
105 #include "CedarPch.h"
106 
107 // Password encryption
SecurePassword(void * secure_password,void * password,void * random)108 void SecurePassword(void *secure_password, void *password, void *random)
109 {
110 	BUF *b;
111 	// Validate arguments
112 	if (secure_password == NULL || password == NULL || random == NULL)
113 	{
114 		return;
115 	}
116 
117 	b = NewBuf();
118 	WriteBuf(b, password, SHA1_SIZE);
119 	WriteBuf(b, random, SHA1_SIZE);
120 	Hash(secure_password, b->Buf, b->Size, true);
121 
122 	FreeBuf(b);
123 }
124 
125 // Generate 160bit random number
GenRamdom(void * random)126 void GenRamdom(void *random)
127 {
128 	// Validate arguments
129 	if (random == NULL)
130 	{
131 		return;
132 	}
133 
134 	Rand(random, SHA1_SIZE);
135 }
136 
137 // Anonymous authentication of user
SamAuthUserByAnonymous(HUB * h,char * username)138 bool SamAuthUserByAnonymous(HUB *h, char *username)
139 {
140 	bool b = false;
141 	// Validate arguments
142 	if (h == NULL || username == NULL)
143 	{
144 		return false;
145 	}
146 
147 	AcLock(h);
148 	{
149 		USER *u = AcGetUser(h, username);
150 		if (u)
151 		{
152 			Lock(u->lock);
153 			{
154 				if (u->AuthType == AUTHTYPE_ANONYMOUS)
155 				{
156 					b = true;
157 				}
158 			}
159 			Unlock(u->lock);
160 		}
161 		ReleaseUser(u);
162 	}
163 	AcUnlock(h);
164 
165 	return b;
166 }
167 
168 // Plaintext password authentication of user
SamAuthUserByPlainPassword(CONNECTION * c,HUB * hub,char * username,char * password,bool ast,UCHAR * mschap_v2_server_response_20,RADIUS_LOGIN_OPTION * opt)169 bool SamAuthUserByPlainPassword(CONNECTION *c, HUB *hub, char *username, char *password, bool ast, UCHAR *mschap_v2_server_response_20, RADIUS_LOGIN_OPTION *opt)
170 {
171 	bool b = false;
172 	wchar_t *name = NULL;
173 	bool auth_by_nt = false;
174 	HUB *h;
175 	// Validate arguments
176 	if (hub == NULL || c == NULL || username == NULL)
177 	{
178 		return false;
179 	}
180 
181 	if (GetGlobalServerFlag(GSF_DISABLE_RADIUS_AUTH) != 0)
182 	{
183 		return false;
184 	}
185 
186 	h = hub;
187 
188 	AddRef(h->ref);
189 
190 	// Get the user name on authentication system
191 	AcLock(hub);
192 	{
193 		USER *u;
194 		u = AcGetUser(hub, ast == false ? username : "*");
195 		if (u)
196 		{
197 			Lock(u->lock);
198 			{
199 				if (u->AuthType == AUTHTYPE_RADIUS)
200 				{
201 					// Radius authentication
202 					AUTHRADIUS *auth = (AUTHRADIUS *)u->AuthData;
203 					if (ast || auth->RadiusUsername == NULL || UniStrLen(auth->RadiusUsername) == 0)
204 					{
205 						if( IsEmptyStr(h->RadiusRealm) == false )
206 						{
207 							char name_and_realm[MAX_SIZE];
208 							StrCpy(name_and_realm, sizeof(name_and_realm), username);
209 							StrCat(name_and_realm, sizeof(name_and_realm), "@");
210 							StrCat(name_and_realm, sizeof(name_and_realm), h->RadiusRealm);
211 							name = CopyStrToUni(name_and_realm);
212 						}
213 						else
214 						{
215 							name = CopyStrToUni(username);
216 						}
217 					}
218 					else
219 					{
220 						name = CopyUniStr(auth->RadiusUsername);
221 					}
222 					auth_by_nt = false;
223 				}
224 				else if (u->AuthType == AUTHTYPE_NT)
225 				{
226 					// NT authentication
227 					AUTHNT *auth = (AUTHNT *)u->AuthData;
228 					if (ast || auth->NtUsername == NULL || UniStrLen(auth->NtUsername) == 0)
229 					{
230 						name = CopyStrToUni(username);
231 					}
232 					else
233 					{
234 						name = CopyUniStr(auth->NtUsername);
235 					}
236 					auth_by_nt = true;
237 				}
238 			}
239 			Unlock(u->lock);
240 			ReleaseUser(u);
241 		}
242 	}
243 	AcUnlock(hub);
244 
245 	if (name != NULL)
246 	{
247 		if (auth_by_nt == false)
248 		{
249 			// Radius authentication
250 			char radius_server_addr[MAX_SIZE];
251 			UINT radius_server_port;
252 			char radius_secret[MAX_SIZE];
253 			char suffix_filter[MAX_SIZE];
254 			wchar_t suffix_filter_w[MAX_SIZE];
255 			UINT interval;
256 
257 			Zero(suffix_filter, sizeof(suffix_filter));
258 			Zero(suffix_filter_w, sizeof(suffix_filter_w));
259 
260 			// Get the Radius server information
261 			if (GetRadiusServerEx2(hub, radius_server_addr, sizeof(radius_server_addr), &radius_server_port, radius_secret, sizeof(radius_secret), &interval, suffix_filter, sizeof(suffix_filter)))
262 			{
263 				Unlock(hub->lock);
264 
265 				StrToUni(suffix_filter_w, sizeof(suffix_filter_w), suffix_filter);
266 
267 				if (UniIsEmptyStr(suffix_filter_w) || UniEndWith(name, suffix_filter_w))
268 				{
269 					// Attempt to login
270 					b = RadiusLogin(c, radius_server_addr, radius_server_port,
271 						radius_secret, StrLen(radius_secret),
272 						name, password, interval, mschap_v2_server_response_20, opt, hub->Name);
273 
274 					if (b)
275 					{
276 						if (opt != NULL)
277 						{
278 							opt->Out_IsRadiusLogin = true;
279 						}
280 					}
281 				}
282 
283 				Lock(hub->lock);
284 			}
285 			else
286 			{
287 				HLog(hub, "LH_NO_RADIUS_SETTING", name);
288 			}
289 		}
290 		else
291 		{
292 			// NT authentication (Not available for non-Win32)
293 #ifdef	OS_WIN32
294 			IPC_MSCHAP_V2_AUTHINFO mschap;
295 			Unlock(hub->lock);
296 
297 			if (ParseAndExtractMsChapV2InfoFromPassword(&mschap, password) == false)
298 			{
299 				// Plaintext password authentication
300 				b = MsCheckLogon(name, password);
301 			}
302 			else
303 			{
304 				UCHAR challenge8[8];
305 				UCHAR nt_pw_hash_hash[16];
306 				char nt_name[MAX_SIZE];
307 
308 				UniToStr(nt_name, sizeof(nt_name), name);
309 
310 				// MS-CHAPv2 authentication
311 				MsChapV2_GenerateChallenge8(challenge8, mschap.MsChapV2_ClientChallenge,
312 					mschap.MsChapV2_ServerChallenge,
313 					mschap.MsChapV2_PPPUsername);
314 
315 				Debug("MsChapV2_PPPUsername = %s, nt_name = %s\n", mschap.MsChapV2_PPPUsername, nt_name);
316 
317 				b = MsPerformMsChapV2AuthByLsa(nt_name, challenge8, mschap.MsChapV2_ClientResponse, nt_pw_hash_hash);
318 
319 				if (b)
320 				{
321 					if (mschap_v2_server_response_20 != NULL)
322 					{
323 						MsChapV2Server_GenerateResponse(mschap_v2_server_response_20, nt_pw_hash_hash,
324 							mschap.MsChapV2_ClientResponse, challenge8);
325 					}
326 				}
327 			}
328 
329 			Lock(hub->lock);
330 #else	// OS_WIN32
331 			// Nothing to do other than Win32
332 #endif	// OS_WIN32
333 		}
334 
335 		// Memory release
336 		Free(name);
337 	}
338 
339 	ReleaseHub(h);
340 
341 	return b;
342 }
343 
344 // Certificate authentication of user
SamAuthUserByCert(HUB * h,char * username,X * x)345 bool SamAuthUserByCert(HUB *h, char *username, X *x)
346 {
347 	bool b = false;
348 	// Validate arguments
349 	if (h == NULL || username == NULL || x == NULL)
350 	{
351 		return false;
352 	}
353 
354 	if (GetGlobalServerFlag(GSF_DISABLE_CERT_AUTH) != 0)
355 	{
356 		return false;
357 	}
358 
359 	// Check expiration date
360 	if (CheckXDateNow(x) == false)
361 	{
362 		return false;
363 	}
364 
365 	// Check the Certification Revocation List
366 	if (IsValidCertInHub(h, x) == false)
367 	{
368 		// Bad
369 		wchar_t tmp[MAX_SIZE * 2];
370 
371 		// Log the contents of the certificate
372 		GetAllNameFromX(tmp, sizeof(tmp), x);
373 
374 		HLog(h, "LH_AUTH_NG_CERT", username, tmp);
375 		return false;
376 	}
377 
378 	AcLock(h);
379 	{
380 		USER *u;
381 		u = AcGetUser(h, username);
382 		if (u)
383 		{
384 			Lock(u->lock);
385 			{
386 				if (u->AuthType == AUTHTYPE_USERCERT)
387 				{
388 					// Check whether to matche with the registered certificate
389 					AUTHUSERCERT *auth = (AUTHUSERCERT *)u->AuthData;
390 					if (CompareX(auth->UserX, x))
391 					{
392 						b = true;
393 					}
394 				}
395 				else if (u->AuthType == AUTHTYPE_ROOTCERT)
396 				{
397 					// Check whether the certificate has been signed by the root certificate
398 					AUTHROOTCERT *auth = (AUTHROOTCERT *)u->AuthData;
399 					if (h->HubDb != NULL)
400 					{
401 						LockList(h->HubDb->RootCertList);
402 						{
403 							X *root_cert;
404 							root_cert = GetIssuerFromList(h->HubDb->RootCertList, x);
405 							if (root_cert != NULL)
406 							{
407 								b = true;
408 								if (auth->CommonName != NULL && UniIsEmptyStr(auth->CommonName) == false)
409 								{
410 									// Compare the CN
411 									if (UniStrCmpi(x->subject_name->CommonName, auth->CommonName) != 0)
412 									{
413 										b = false;
414 									}
415 								}
416 								if (auth->Serial != NULL && auth->Serial->size >= 1)
417 								{
418 									// Compare the serial number
419 									if (CompareXSerial(x->serial, auth->Serial) == false)
420 									{
421 										b = false;
422 									}
423 								}
424 							}
425 						}
426 						UnlockList(h->HubDb->RootCertList);
427 					}
428 				}
429 			}
430 			Unlock(u->lock);
431 			ReleaseUser(u);
432 		}
433 	}
434 	AcUnlock(h);
435 
436 	if (b)
437 	{
438 		wchar_t tmp[MAX_SIZE * 2];
439 
440 		// Log the contents of the certificate
441 		GetAllNameFromX(tmp, sizeof(tmp), x);
442 
443 		HLog(h, "LH_AUTH_OK_CERT", username, tmp);
444 	}
445 
446 	return b;
447 }
448 
449 // Get the root certificate that signed the specified certificate from the list
GetIssuerFromList(LIST * cert_list,X * cert)450 X *GetIssuerFromList(LIST *cert_list, X *cert)
451 {
452 	UINT i;
453 	X *ret = NULL;
454 	// Validate arguments
455 	if (cert_list == NULL || cert == NULL)
456 	{
457 		return NULL;
458 	}
459 
460 	for (i = 0;i < LIST_NUM(cert_list);i++)
461 	{
462 		X *x = LIST_DATA(cert_list, i);
463 		// Name comparison
464 		if (CheckXDateNow(x))
465 		{
466 			if (CompareName(x->subject_name, cert->issuer_name))
467 			{
468 				// Get the public key of the root certificate
469 				K *k = GetKFromX(x);
470 
471 				if (k != NULL)
472 				{
473 					// Check the signature
474 					if (CheckSignature(cert, k))
475 					{
476 						ret = x;
477 					}
478 					FreeK(k);
479 				}
480 			}
481 		}
482 		if (CompareX(x, cert))
483 		{
484 			// Complete identical
485 			ret = x;
486 		}
487 	}
488 
489 	return ret;
490 }
491 
492 // Get the policy to be applied for the user
SamGetUserPolicy(HUB * h,char * username)493 POLICY *SamGetUserPolicy(HUB *h, char *username)
494 {
495 	POLICY *ret = NULL;
496 	// Validate arguments
497 	if (h == NULL || username == NULL)
498 	{
499 		return NULL;
500 	}
501 
502 	AcLock(h);
503 	{
504 		USER *u;
505 		u = AcGetUser(h, username);
506 		if (u)
507 		{
508 			USERGROUP *g = NULL;
509 			Lock(u->lock);
510 			{
511 				if (u->Policy != NULL)
512 				{
513 					ret = ClonePolicy(u->Policy);
514 				}
515 
516 				g = u->Group;
517 
518 				if (g != NULL)
519 				{
520 					AddRef(g->ref);
521 				}
522 			}
523 			Unlock(u->lock);
524 
525 			ReleaseUser(u);
526 			u = NULL;
527 
528 			if (ret == NULL)
529 			{
530 				if (g != NULL)
531 				{
532 					Lock(g->lock);
533 					{
534 						ret = ClonePolicy(g->Policy);
535 					}
536 					Unlock(g->lock);
537 				}
538 			}
539 
540 			if (g != NULL)
541 			{
542 				ReleaseGroup(g);
543 			}
544 		}
545 	}
546 	AcUnlock(h);
547 
548 	return ret;
549 }
550 
551 // Password authentication of user
SamAuthUserByPassword(HUB * h,char * username,void * random,void * secure_password,char * mschap_v2_password,UCHAR * mschap_v2_server_response_20,UINT * err)552 bool SamAuthUserByPassword(HUB *h, char *username, void *random, void *secure_password, char *mschap_v2_password, UCHAR *mschap_v2_server_response_20, UINT *err)
553 {
554 	bool b = false;
555 	UCHAR secure_password_check[SHA1_SIZE];
556 	bool is_mschap = false;
557 	IPC_MSCHAP_V2_AUTHINFO mschap;
558 	UINT dummy = 0;
559 	// Validate arguments
560 	if (h == NULL || username == NULL || secure_password == NULL)
561 	{
562 		return false;
563 	}
564 	if (err == NULL)
565 	{
566 		err = &dummy;
567 	}
568 
569 	*err = 0;
570 
571 	Zero(&mschap, sizeof(mschap));
572 
573 	is_mschap = ParseAndExtractMsChapV2InfoFromPassword(&mschap, mschap_v2_password);
574 
575 	if (StrCmpi(username, ADMINISTRATOR_USERNAME) == 0)
576 	{
577 		// Administrator mode
578 		SecurePassword(secure_password_check, h->SecurePassword, random);
579 		if (Cmp(secure_password_check, secure_password, SHA1_SIZE) == 0)
580 		{
581 			return true;
582 		}
583 		else
584 		{
585 			return false;
586 		}
587 	}
588 
589 	AcLock(h);
590 	{
591 		USER *u;
592 		u = AcGetUser(h, username);
593 		if (u)
594 		{
595 			Lock(u->lock);
596 			{
597 				if (u->AuthType == AUTHTYPE_PASSWORD)
598 				{
599 					AUTHPASSWORD *auth = (AUTHPASSWORD *)u->AuthData;
600 
601 					if (is_mschap == false)
602 					{
603 						// Normal password authentication
604 						SecurePassword(secure_password_check, auth->HashedKey, random);
605 						if (Cmp(secure_password_check, secure_password, SHA1_SIZE) == 0)
606 						{
607 							b = true;
608 						}
609 					}
610 					else
611 					{
612 						// MS-CHAP v2 authentication via PPP
613 						UCHAR challenge8[8];
614 						UCHAR client_response[24];
615 
616 						if (IsZero(auth->NtLmSecureHash, MD5_SIZE))
617 						{
618 							// NTLM hash is not registered in the user account
619 							*err = ERR_MSCHAP2_PASSWORD_NEED_RESET;
620 						}
621 						else
622 						{
623 							UCHAR nt_pw_hash_hash[16];
624 							Zero(challenge8, sizeof(challenge8));
625 							Zero(client_response, sizeof(client_response));
626 
627 							MsChapV2_GenerateChallenge8(challenge8, mschap.MsChapV2_ClientChallenge, mschap.MsChapV2_ServerChallenge,
628 								mschap.MsChapV2_PPPUsername);
629 
630 							MsChapV2Client_GenerateResponse(client_response, challenge8, auth->NtLmSecureHash);
631 
632 							if (Cmp(client_response, mschap.MsChapV2_ClientResponse, 24) == 0)
633 							{
634 								// Hash matched
635 								b = true;
636 
637 								// Calculate the response
638 								GenerateNtPasswordHashHash(nt_pw_hash_hash, auth->NtLmSecureHash);
639 								MsChapV2Server_GenerateResponse(mschap_v2_server_response_20, nt_pw_hash_hash,
640 									client_response, challenge8);
641 							}
642 						}
643 					}
644 				}
645 			}
646 			Unlock(u->lock);
647 			ReleaseUser(u);
648 		}
649 	}
650 	AcUnlock(h);
651 
652 	return b;
653 }
654 
655 // Make sure that the user exists
SamIsUser(HUB * h,char * username)656 bool SamIsUser(HUB *h, char *username)
657 {
658 	bool b;
659 	// Validate arguments
660 	if (h == NULL || username == NULL)
661 	{
662 		return false;
663 	}
664 
665 	AcLock(h);
666 	{
667 		b = AcIsUser(h, username);
668 	}
669 	AcUnlock(h);
670 
671 	return b;
672 }
673 
674 // Get the type of authentication used by the user
SamGetUserAuthType(HUB * h,char * username)675 UINT SamGetUserAuthType(HUB *h, char *username)
676 {
677 	UINT authtype;
678 	// Validate arguments
679 	if (h == NULL || username == NULL)
680 	{
681 		return INFINITE;
682 	}
683 
684 	AcLock(h);
685 	{
686 		USER *u = AcGetUser(h, username);
687 		if (u == NULL)
688 		{
689 			authtype = INFINITE;
690 		}
691 		else
692 		{
693 			authtype = u->AuthType;
694 			ReleaseUser(u);
695 		}
696 	}
697 	AcUnlock(h);
698 
699 	return authtype;
700 }
701 
702