From: Selva Nair <selva.n...@gmail.com> Currently the username unqualified by the domain is used to validate a user which fails for domain users. Instead compare the user's SID with SIDs in the Administrators group and ovpn_admin_group.
This has the advantage that connection to a domain controller is not required and will work even when user is logged in with cached credentials. Limitations: (i) Group membership is not checked recursively (ii) Domain administrators will not be recognized as members of local Administrators group. Resolves Trac: #810 Signed-off-by: Selva Nair <selva.n...@gmail.com> --- src/openvpnserv/validate.c | 95 +++++++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 40 deletions(-) diff --git a/src/openvpnserv/validate.c b/src/openvpnserv/validate.c index c9c3855..98d868f 100644 --- a/src/openvpnserv/validate.c +++ b/src/openvpnserv/validate.c @@ -49,6 +49,8 @@ static const WCHAR *white_list[] = NULL /* last value */ }; +static BOOL IsUserInGroup(PSID sid, const WCHAR *group_name); + /* * Check workdir\fname is inside config_dir * The logic here is simple: we may reject some valid paths if ..\ is in any of the strings @@ -152,16 +154,11 @@ GetBuiltinAdminGroupName(WCHAR *name, DWORD nlen) BOOL IsAuthorizedUser(SID *sid, settings_t *s) { - LOCALGROUP_USERS_INFO_0 *groups = NULL; - DWORD nread; - DWORD nmax; - WCHAR *tmp = NULL; const WCHAR *admin_group[2]; WCHAR username[MAX_NAME]; WCHAR domain[MAX_NAME]; WCHAR sysadmin_group[MAX_NAME]; - DWORD err, len = MAX_NAME; - int i; + DWORD len = MAX_NAME; BOOL ret = FALSE; SID_NAME_USE sid_type; @@ -169,17 +166,9 @@ IsAuthorizedUser(SID *sid, settings_t *s) if (!LookupAccountSidW(NULL, sid, username, &len, domain, &len, &sid_type)) { MsgToEventLog(M_SYSERR, TEXT("LookupAccountSid")); - goto out; - } - - /* Get an array of groups the user is member of */ - err = NetUserGetLocalGroups(NULL, username, 0, LG_INCLUDE_INDIRECT, (LPBYTE *) &groups, - MAX_PREFERRED_LENGTH, &nread, &nmax); - if (err && err != ERROR_MORE_DATA) - { - SetLastError(err); - MsgToEventLog(M_SYSERR, TEXT("NetUserGetLocalGroups")); - goto out; + /* not fatal as this is now used only for logging */ + username[0] = '\0'; + domain[0] = '\0'; } if (GetBuiltinAdminGroupName(sysadmin_group, _countof(sysadmin_group))) @@ -193,40 +182,66 @@ IsAuthorizedUser(SID *sid, settings_t *s) admin_group[0] = SYSTEM_ADMIN_GROUP; } -#ifdef UNICODE admin_group[1] = s->ovpn_admin_group; -#else - tmp = NULL; - len = MultiByteToWideChar(CP_UTF8, 0, s->ovpn_admin_group, -1, NULL, 0); - if (len == 0 || (tmp = malloc(len*sizeof(WCHAR))) == NULL) - { - MsgToEventLog(M_SYSERR, TEXT("Failed to convert admin group name to WideChar")); - goto out; - } - MultiByteToWideChar(CP_UTF8, 0, s->ovpn_admin_group, -1, tmp, len); - admin_group[1] = tmp; -#endif - /* Check if user's groups include any of the admin groups */ - for (i = 0; i < nread; i++) + for (int i = 0; i < 2; ++i) { - if (wcscmp(groups[i].lgrui0_name, admin_group[0]) == 0 - || wcscmp(groups[i].lgrui0_name, admin_group[1]) == 0 - ) + ret = IsUserInGroup(sid, admin_group[i]); + if (ret) { - MsgToEventLog(M_INFO, TEXT("Authorizing user %s by virtue of membership in group %s"), - username, groups[i].lgrui0_name); - ret = TRUE; - break; + MsgToEventLog(M_INFO, TEXT("Authorizing user '%s@%s' by virtue of membership in group '%s'"), + username, domain, admin_group[i]); + goto out; } } out: - if (groups) + return ret; +} + +/** + * Check a user with specified sid is in a local group named group_name + * + * Using sid instead of username avoids reference to domains so that + * this could be completed without access to the Domain Controller. + * + * Returns true if the user is in the group, false otherwise. + */ +static BOOL +IsUserInGroup(PSID sid, const WCHAR *group_name) +{ + BOOL ret = FALSE; + LOCALGROUP_MEMBERS_INFO_0 *groups; + DWORD_PTR resume = 0; + DWORD err, nread, nmax; + int nloop = 0; /* a counter used to not get stuck in the do .. while() */ + + do { + groups = NULL; + err = NetLocalGroupGetMembers(NULL, group_name, 0, (LPBYTE *) &groups, + MAX_PREFERRED_LENGTH, &nread, &nmax, &resume); + if (err && err != ERROR_MORE_DATA) + { + break; + } + + /* If a match is already found, ret = TRUE, the loop is skipped */ + for (int i = 0; i < nread && !ret; ++i) + { + ret = EqualSid(groups[i].lgrmi0_sid, sid); + } + NetApiBufferFree(groups); + + /* MSDN says the lookup should always iterate until err != ERROR_MORE_DATA */ + } while (err == ERROR_MORE_DATA && nloop++ < 100); + + if (err != NERR_Success && err != NERR_GroupNotFound) + { + SetLastError(err); + MsgToEventLog(M_SYSERR, TEXT("In NetLocalGroupGetMembers for group '%s'"), group_name); } - free(tmp); return ret; } -- 2.1.4 ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, SlashDot.org! http://sdm.link/slashdot _______________________________________________ Openvpn-devel mailing list Openvpn-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openvpn-devel