summaryrefslogtreecommitdiff
path: root/src/openvpnserv/validate.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvpnserv/validate.c')
-rw-r--r--src/openvpnserv/validate.c178
1 files changed, 47 insertions, 131 deletions
diff --git a/src/openvpnserv/validate.c b/src/openvpnserv/validate.c
index f6a97e9..c9c3855 100644
--- a/src/openvpnserv/validate.c
+++ b/src/openvpnserv/validate.c
@@ -16,9 +16,10 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "validate.h"
@@ -48,9 +49,6 @@ static const WCHAR *white_list[] =
NULL /* last value */
};
-static BOOL IsUserInGroup(PSID sid, const PTOKEN_GROUPS groups, const WCHAR *group_name);
-static PTOKEN_GROUPS GetTokenGroups(const HANDLE token);
-
/*
* 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
@@ -149,16 +147,21 @@ GetBuiltinAdminGroupName(WCHAR *name, DWORD nlen)
/*
* Check whether user is a member of Administrators group or
- * the group specified in ovpn_admin_group
+ * the group specified in s->ovpn_admin_group
*/
BOOL
-IsAuthorizedUser(PSID sid, const HANDLE token, const WCHAR *ovpn_admin_group)
+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 len = MAX_NAME;
+ DWORD err, len = MAX_NAME;
+ int i;
BOOL ret = FALSE;
SID_NAME_USE sid_type;
@@ -166,9 +169,17 @@ IsAuthorizedUser(PSID sid, const HANDLE token, const WCHAR *ovpn_admin_group)
if (!LookupAccountSidW(NULL, sid, username, &len, domain, &len, &sid_type))
{
MsgToEventLog(M_SYSERR, TEXT("LookupAccountSid"));
- /* not fatal as this is now used only for logging */
- username[0] = '\0';
- domain[0] = '\0';
+ 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;
}
if (GetBuiltinAdminGroupName(sysadmin_group, _countof(sysadmin_group)))
@@ -181,136 +192,41 @@ IsAuthorizedUser(PSID sid, const HANDLE token, const WCHAR *ovpn_admin_group)
/* use the default value */
admin_group[0] = SYSTEM_ADMIN_GROUP;
}
- admin_group[1] = ovpn_admin_group;
-
- PTOKEN_GROUPS token_groups = GetTokenGroups(token);
- for (int i = 0; i < 2; ++i)
- {
- ret = IsUserInGroup(sid, token_groups, admin_group[i]);
- if (ret)
- {
- MsgToEventLog(M_INFO, TEXT("Authorizing user '%s@%s' by virtue of membership in group '%s'"),
- username, domain, admin_group[i]);
- goto out;
- }
- }
-
-out:
- free(token_groups);
- return ret;
-}
-
-/**
- * Get a list of groups in token.
- * Returns a pointer to TOKEN_GROUPS struct or NULL on error.
- * The caller should free the returned pointer.
- */
-static PTOKEN_GROUPS
-GetTokenGroups(const HANDLE token)
-{
- PTOKEN_GROUPS groups = NULL;
- DWORD buf_size = 0;
-
- if (!GetTokenInformation(token, TokenGroups, groups, buf_size, &buf_size)
- && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
- {
- groups = malloc(buf_size);
- }
- if (!groups)
- {
- MsgToEventLog(M_SYSERR, L"GetTokenGroups");
- }
- else if (!GetTokenInformation(token, TokenGroups, groups, buf_size, &buf_size))
- {
- MsgToEventLog(M_SYSERR, L"GetTokenInformation");
- free(groups);
- }
- return groups;
-}
-/*
- * Find SID from name
- *
- * On input sid buffer should have space for at least sid_size bytes.
- * Returns true on success, false on failure.
- * Suggest: in caller allocate sid to hold SECURITY_MAX_SID_SIZE bytes
- */
-static BOOL
-LookupSID(const WCHAR *name, PSID sid, DWORD sid_size)
-{
- SID_NAME_USE su;
- WCHAR domain[MAX_NAME];
- DWORD dlen = _countof(domain);
-
- if (!LookupAccountName(NULL, name, sid, &sid_size, domain, &dlen, &su))
- {
- return FALSE; /* not fatal as the group may not exist */
- }
- return TRUE;
-}
-
-/**
- * User is in group if the token groups contain the SID of the group
- * of if the user is a direct member of the group. The latter check
- * catches dynamic changes in group membership in the local user
- * database not reflected in the token.
- * If token_groups or sid is NULL the corresponding check is skipped.
- *
- * Using sid and list of groups in token avoids reference to domains so that
- * this could be completed without access to a Domain Controller.
- *
- * Returns true if the user is in the group, false otherwise.
- */
-static BOOL
-IsUserInGroup(PSID sid, const PTOKEN_GROUPS token_groups, const WCHAR *group_name)
-{
- BOOL ret = FALSE;
- DWORD_PTR resume = 0;
- DWORD err;
- BYTE grp_sid[SECURITY_MAX_SID_SIZE];
- int nloop = 0; /* a counter used to not get stuck in the do .. while() */
-
- /* first check in the token groups */
- if (token_groups && LookupSID(group_name, (PSID) grp_sid, _countof(grp_sid)))
+#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)
{
- for (DWORD i = 0; i < token_groups->GroupCount; ++i)
- {
- if (EqualSid((PSID) grp_sid, token_groups->Groups[i].Sid))
- {
- return TRUE;
- }
- }
+ 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 user's SID is a member of the group */
- if (!sid)
- {
- return FALSE;
- }
- do
+ /* Check if user's groups include any of the admin groups */
+ for (i = 0; i < nread; i++)
{
- DWORD nread, nmax;
- LOCALGROUP_MEMBERS_INFO_0 *members = NULL;
- err = NetLocalGroupGetMembers(NULL, group_name, 0, (LPBYTE *) &members,
- MAX_PREFERRED_LENGTH, &nread, &nmax, &resume);
- if ((err != NERR_Success && err != ERROR_MORE_DATA))
+ if (wcscmp(groups[i].lgrui0_name, admin_group[0]) == 0
+ || wcscmp(groups[i].lgrui0_name, admin_group[1]) == 0
+ )
{
+ MsgToEventLog(M_INFO, TEXT("Authorizing user %s by virtue of membership in group %s"),
+ username, groups[i].lgrui0_name);
+ ret = TRUE;
break;
}
- /* If a match is already found, ret == TRUE and the loop is skipped */
- for (int i = 0; i < nread && !ret; ++i)
- {
- ret = EqualSid(members[i].lgrmi0_sid, sid);
- }
- NetApiBufferFree(members);
- /* 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)
+out:
+ if (groups)
{
- SetLastError(err);
- MsgToEventLog(M_SYSERR, TEXT("In NetLocalGroupGetMembers for group '%s'"), group_name);
+ NetApiBufferFree(groups);
}
+ free(tmp);
return ret;
}