/***************************************************************************
* CVSID: $Id: policy.c,v 1.1 2006-03-14 06:14:33 david Exp $
*
* policy.c : Wraps policy
*
* Copyright (C) 2006 David Zeuthen, <david@fubar.dk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
**************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <errno.h>
#include <glib.h>
#include "policy.h"
#ifdef __SUNPRO_C
#define __FUNCTION__ __func__
#endif
static char *policy_directory = PACKAGE_SYSCONF_DIR "/PolicyKit/privilege.d";
void
policy_util_set_policy_directory (const char *directory)
{
policy_directory = g_strdup (directory);
}
typedef enum {
POLICY_ELEMENT_TYPE_UID,
POLICY_ELEMENT_TYPE_GID
} PolicyElementType;
struct PolicyElement_s
{
PolicyElementType type;
union {
uid_t uid;
gid_t gid;
} id;
gboolean include_all;
gboolean exclude_all;
char *resource;
};
typedef struct PolicyElement_s PolicyElement;
static PolicyElement *
policy_element_new (void)
{
PolicyElement *elem;
elem = g_new0 (PolicyElement, 1);
return elem;
}
static void
policy_element_free (PolicyElement *elem)
{
g_free (elem->resource);
g_free (elem);
}
static void
policy_element_free_list (GList *policy_element_list)
{
GList *l;
for (l = policy_element_list; l != NULL; l = g_list_next (l)) {
PolicyElement *elem = (PolicyElement *) l->data;
policy_element_free (elem);
}
g_list_free (policy_element_list);
}
#if 0
static void
policy_element_dump (PolicyElement *elem, FILE* fp)
{
char *t;
if (elem->type == POLICY_ELEMENT_TYPE_UID)
t = "uid";
else if (elem->type == POLICY_ELEMENT_TYPE_GID)
t = "gid";
else
t = "(Unknown)";
fprintf (fp, "type: %s\n", t);
if (elem->type == POLICY_ELEMENT_TYPE_UID) {
if (elem->include_all) {
fprintf (fp, "uid: all\n");
} else if (elem->exclude_all) {
fprintf (fp, "uid: none\n");
} else {
fprintf (fp, "uid: %d\n", (int) elem->id.uid);
}
} else if (elem->type == POLICY_ELEMENT_TYPE_GID) {
if (elem->include_all) {
fprintf (fp, "gid: all\n");
} else if (elem->exclude_all) {
fprintf (fp, "gid: none\n");
} else {
fprintf (fp, "gid: %d\n", (int) elem->id.gid);
}
}
fprintf (fp, "resource: %s\n", elem->resource != NULL ? elem->resource : "(None)");
}
#endif
static PolicyResult
txt_backend_read_policy (const char *policy,
const char *key,
GList **result)
{
int i;
GKeyFile *keyfile;
GError *error;
PolicyResult rc;
char *path;
char *value = NULL;
char **tokens = NULL;
char *ttype = NULL;
char *tvalue = NULL;
char *tresource = NULL;
PolicyElement *elem = NULL;
GList *res;
GList *l;
char *token;
error = NULL;
rc = POLICY_RESULT_ERROR;
res = NULL;
*result = NULL;
keyfile = g_key_file_new ();
path = g_strdup_printf ("%s/%s.privilege", policy_directory, policy);
/*g_message ("Loading %s", path);*/
if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, &error)) {
g_warning ("Couldn't open key-file '%s': %s", path, error->message);
g_error_free (error);
rc = POLICY_RESULT_NO_SUCH_POLICY;
goto out;
}
value = g_key_file_get_string (keyfile, "Policy", key, &error);
if (value == NULL) {
g_warning ("Cannot get key '%s' in group 'Policy' in file '%s': %s", key, path, error->message);
g_error_free (error);
rc = POLICY_RESULT_ERROR;
goto out;
}
/*g_message ("value = '%s'", value);*/
tokens = g_strsplit (value, " ", 0);
for (i = 0; tokens[i] != NULL; i++) {
char **components;
int num_components;
token = tokens[i];
/*g_message (" token = '%s'", token);*/
ttype = NULL;
tvalue = NULL;
tresource = NULL;
elem = policy_element_new ();
components = g_strsplit (token, ":", 3);
num_components = g_strv_length (components);
if (num_components == 2) {
ttype = g_strdup (components[0]);
tvalue = g_strdup (components[1]);
tresource = NULL;
} else if (num_components == 3) {
ttype = g_strdup (components[0]);
tvalue = g_strdup (components[1]);
tresource = g_strdup (components[2]);
} else {
g_strfreev (components);
goto malformed_token;
}
g_strfreev (components);
/*g_message (" type='%s' value='%s' resource='%s'", ttype, tvalue, tresource != NULL ? tresource : "None");*/
if (strcmp (ttype, "uid") == 0) {
elem->type = POLICY_ELEMENT_TYPE_UID;
if (strcmp (tvalue, "__all__") == 0) {
elem->include_all = TRUE;
} else if (strcmp (tvalue, "__none__") == 0) {
elem->exclude_all = TRUE;
} else {
uid_t uid;
char *endp;
uid = (uid_t) g_ascii_strtoull (tvalue, &endp, 0);
if (endp[0] != '\0') {
uid = policy_util_name_to_uid (tvalue, NULL);
if (uid == (uid_t) -1) {
g_warning ("User '%s' does not exist", tvalue);
goto malformed_token;
}
}
elem->id.uid = uid;
}
} else if (strcmp (ttype, "gid") == 0) {
elem->type = POLICY_ELEMENT_TYPE_GID;
if (strcmp (tvalue, "__all__") == 0) {
elem->include_all = TRUE;
} else if (strcmp (tvalue, "__none__") == 0) {
elem->exclude_all = TRUE;
} else {
gid_t gid;
char *endp;
gid = (gid_t) g_ascii_strtoull (tvalue, &endp, 0);
if (endp[0] != '\0') {
gid = policy_util_name_to_gid (tvalue);
if (gid == (gid_t) -1) {
g_warning ("Group '%s' does not exist", tvalue);
goto malformed_token;
}
}
elem->id.gid = gid;
}
} else {
g_warning ("Token '%s' in key '%s' in group 'Policy' in file '%s' malformed",
token, key, path);
goto malformed_token;
}
if (tresource != NULL) {
elem->resource = g_strdup (tresource);
}
g_free (ttype);
g_free (tvalue);
g_free (tresource);
res = g_list_append (res, elem);
/*policy_element_dump (elem, stderr);*/
}
*result = res;
rc = POLICY_RESULT_OK;
goto out;
malformed_token:
g_warning ("Token '%s' in key '%s' in group 'Policy' in file '%s' malformed", token, key, path);
for (l = res; l != NULL; l = g_list_next (l)) {
policy_element_free ((PolicyElement *) l->data);
}
g_list_free (res);
policy_element_free (elem);
g_free (ttype);
g_free (tvalue);
g_free (tresource);
out:
g_strfreev (tokens);
g_free (value);
g_key_file_free (keyfile);
g_free (path);
return rc;
}
static PolicyResult
policy_get_whitelist (const char *policy,
GList **result)
{
return txt_backend_read_policy (policy, "Allow", result);
}
static PolicyResult
policy_get_blacklist (const char *policy,
GList **result)
{
return txt_backend_read_policy (policy, "Deny", result);
}
/** Return all elements in the white-list for a policy
*
* @param result On success set to a list of dynamically allocated strings.
* Must be freed by the caller.
* @return Whether the operation succeeded
*/
PolicyResult
policy_get_policies (GList **result)
{
GDir *dir;
GError *error;
const char *f;
error = NULL;
*result = NULL;
if ((dir = g_dir_open (policy_directory, 0, &error)) == NULL) {
g_critical ("Unable to open %s: %s", policy_directory, error->message);
g_error_free (error);
goto error;
}
while ((f = g_dir_read_name (dir)) != NULL) {
if (g_str_has_suffix (f, ".privilege")) {
char *s;
int pos;
s = g_strdup (f);
pos = strlen (s) - 10; /* .privilege - 10 chars */
if (pos > 0)
s[pos] = '\0';
*result = g_list_append (*result, s);
}
}
g_dir_close (dir);
return POLICY_RESULT_OK;
error:
return POLICY_RESULT_ERROR;
}
static void
afp_process_elem(PolicyElement *elem, gboolean *flag, uid_t uid, guint num_gids, gid_t *gid_list)
{
/*policy_element_dump (elem, stderr);*/
switch (elem->type) {
case POLICY_ELEMENT_TYPE_UID:
if (elem->include_all) {
*flag = TRUE;
} else if (elem->exclude_all) {
*flag = FALSE;
}else {
if (elem->id.uid == uid)
*flag = TRUE;
}
break;
case POLICY_ELEMENT_TYPE_GID:
if (elem->include_all) {
*flag = TRUE;
} else if (elem->exclude_all) {
*flag = FALSE;
}else {
guint i;
for (i = 0; i < num_gids; i++) {
if (elem->id.gid == gid_list[i])
*flag = TRUE;
}
}
break;
}
}
PolicyResult
policy_get_allowed_resources_for_policy_for_uid_gid (uid_t uid,
guint num_gids,
gid_t *gid_list,
const char *policy,
GList **result)
{
GList *l;
GList *whitelist;
GList *blacklist;
gboolean is_in_whitelist;
gboolean is_in_blacklist;
PolicyResult res;
whitelist = NULL;
blacklist = NULL;
*result = NULL;
res = POLICY_RESULT_ERROR;
res = policy_get_whitelist (policy, &whitelist);
if (res != POLICY_RESULT_OK)
goto out;
res = policy_get_blacklist (policy, &blacklist);
if (res != POLICY_RESULT_OK)
goto out;
is_in_whitelist = FALSE;
is_in_blacklist = FALSE;
/* Algorithm: check each resource in whitelist;
* if allowed, check against blacklist..
* if not in blacklist, push to results
*/
for (l = whitelist; l != NULL; l = g_list_next (l)) {
PolicyElement *elem;
gboolean in_whitelist;
elem = (PolicyElement *) l->data;
if (elem->resource != NULL) {
/* check if we're allowed for this resource */
afp_process_elem (elem, &in_whitelist, uid, num_gids, gid_list);
if (in_whitelist) {
GList *j;
gboolean in_blacklist;
/* in whitelist.. yes.. now check if this resource is in the black list*/
in_blacklist = FALSE;
for (j = blacklist; j != NULL; j = g_list_next (j)) {
PolicyElement *elem2;
elem2 = (PolicyElement *) j->data;
if (elem2->resource != NULL &&
strcmp (elem->resource, elem2->resource) == 0) {
afp_process_elem (elem2, &in_blacklist, uid, num_gids, gid_list);
if (in_blacklist)
break;
}
}
if (in_whitelist && !in_blacklist)
*result = g_list_append (*result, g_strdup (elem->resource));
}
}
}
res = POLICY_RESULT_OK;
out:
if (whitelist != NULL)
policy_element_free_list (whitelist);
if (blacklist != NULL)
policy_element_free_list (blacklist);
return res;
}
PolicyResult
policy_is_uid_gid_allowed_for_policy (uid_t uid,
guint num_gids,
gid_t *gid_list,
const char *policy,
const char *resource,
gboolean *result)
{
gboolean is_in_whitelist;
gboolean is_in_blacklist;
GList *l;
GList *whitelist;
GList *blacklist;
PolicyResult res;
whitelist = NULL;
blacklist = NULL;
res = POLICY_RESULT_ERROR;
res = policy_get_whitelist (policy, &whitelist);
if (res != POLICY_RESULT_OK)
goto out;
res = policy_get_blacklist (policy, &blacklist);
if (res != POLICY_RESULT_OK)
goto out;
is_in_whitelist = FALSE;
is_in_blacklist = FALSE;
/* Algorithm: To succeed.. we must be in the whitelist.. and not in the blacklist */
for (l = whitelist; l != NULL; l = g_list_next (l)) {
PolicyElement *elem;
elem = (PolicyElement *) l->data;
if ((elem->resource == NULL) ||
((resource != NULL) && (strcmp (elem->resource, resource) == 0))) {
afp_process_elem (elem, &is_in_whitelist, uid, num_gids, gid_list);
}
}
for (l = blacklist; l != NULL; l = g_list_next (l)) {
PolicyElement *elem;
elem = (PolicyElement *) l->data;
if ((elem->resource == NULL) ||
((resource != NULL) && (strcmp (elem->resource, resource) == 0))) {
afp_process_elem (elem, &is_in_blacklist, uid, num_gids, gid_list);
}
}
*result = is_in_whitelist && (!is_in_blacklist);
res = POLICY_RESULT_OK;
out:
if (whitelist != NULL)
policy_element_free_list (whitelist);
if (blacklist != NULL)
policy_element_free_list (blacklist);
return res;
}
char *
policy_util_uid_to_name (uid_t uid,
gid_t *default_gid)
{
int rc;
char *res;
char *buf = NULL;
long bufsize;
struct passwd pwd;
struct passwd *pwdp;
res = NULL;
bufsize = sysconf (_SC_GETPW_R_SIZE_MAX);
if (bufsize < 0)
bufsize = 1024;
buf = g_new0 (char, bufsize);
rc = getpwuid_r (uid, &pwd, buf, bufsize, &pwdp);
if (rc != 0 || pwdp == NULL) {
/*g_warning ("getpwuid_r() returned %d", rc);*/
goto out;
}
res = g_strdup (pwdp->pw_name);
if (default_gid != NULL)
*default_gid = pwdp->pw_gid;
out:
g_free (buf);
return res;
}
char *
policy_util_gid_to_name (gid_t gid)
{
int rc;
char *res;
char *buf = NULL;
long bufsize;
struct group gbuf;
struct group *gbufp;
res = NULL;
bufsize = sysconf (_SC_GETGR_R_SIZE_MAX);
if (bufsize < 0)
bufsize = 1024;
buf = g_new0 (char, bufsize);
rc = getgrgid_r (gid, &gbuf, buf, bufsize, &gbufp);
if (rc != 0 || gbufp == NULL) {
/*g_warning ("getgrgid_r() returned %d", rc);*/
goto out;
}
res = g_strdup (gbufp->gr_name);
out:
g_free (buf);
return res;
}
uid_t
policy_util_name_to_uid (const char *username, gid_t *default_gid)
{
int rc;
uid_t res;
char *buf = NULL;
long bufsize;
struct passwd pwd;
struct passwd *pwdp;
res = (uid_t) -1;
bufsize = sysconf (_SC_GETPW_R_SIZE_MAX);
if (bufsize < 0)
bufsize = 1024;
buf = g_new0 (char, bufsize);
rc = getpwnam_r (username, &pwd, buf, bufsize, &pwdp);
if (rc != 0 || pwdp == NULL) {
/*g_warning ("getpwnam_r() returned %d", rc);*/
goto out;
}
res = pwdp->pw_uid;
if (default_gid != NULL)
*default_gid = pwdp->pw_gid;
out:
g_free (buf);
return res;
}
gid_t
policy_util_name_to_gid (const char *groupname)
{
int rc;
gid_t res;
char *buf = NULL;
long bufsize;
struct group gbuf;
struct group *gbufp;
res = (gid_t) -1;
bufsize = sysconf (_SC_GETGR_R_SIZE_MAX);
if (bufsize < 0)
bufsize = 1024;
buf = g_new0 (char, bufsize);
rc = getgrnam_r (groupname, &gbuf, buf, bufsize, &gbufp);
if (rc != 0 || gbufp == NULL) {
/*g_warning ("getgrnam_r() returned %d", rc);*/
goto out;
}
res = gbufp->gr_gid;
out:
g_free (buf);
return res;
}
static int
getgrouplist_ala_linux (const char *name,
gid_t basegid,
gid_t *groups,
int *ngroups)
{
if (groups)
return getgrouplist (name, basegid, groups, ngroups);
else {
for (*ngroups = 1;; (*ngroups)++) {
gid_t _groups[*ngroups];
if (getgrouplist (name, basegid, _groups, ngroups) != -1)
return 0;
}
}
}
PolicyResult
policy_get_allowed_resources_for_policy_for_uid (uid_t uid,
const char *policy,
GList **result)
{
int num_groups = 0;
gid_t *groups = NULL;
char *username;
gid_t default_gid;
PolicyResult r;
r = POLICY_RESULT_ERROR;
if ((username = policy_util_uid_to_name (uid, &default_gid)) == NULL)
goto out;
if (getgrouplist_ala_linux(username, default_gid, NULL, &num_groups) < 0) {
groups = (gid_t *) g_new0 (gid_t, num_groups);
if (getgrouplist_ala_linux(username, default_gid, groups, &num_groups) < 0) {
g_warning ("getgrouplist() failed");
goto out;
}
}
r = policy_get_allowed_resources_for_policy_for_uid_gid (uid,
num_groups,
groups,
policy,
result);
out:
g_free (username);
g_free (groups);
return r;
}
PolicyResult
policy_is_uid_allowed_for_policy (uid_t uid,
const char *policy,
const char *resource,
gboolean *result)
{
int num_groups = 0;
gid_t *groups = NULL;
char *username;
gid_t default_gid;
PolicyResult r;
r = POLICY_RESULT_ERROR;
if ((username = policy_util_uid_to_name (uid, &default_gid)) == NULL)
goto out;
if (getgrouplist_ala_linux(username, default_gid, NULL, &num_groups) < 0) {
groups = (gid_t *) g_new0 (gid_t, num_groups);
if (getgrouplist_ala_linux(username, default_gid, groups, &num_groups) < 0) {
g_warning ("getgrouplist() failed");
goto out;
}
}
r = policy_is_uid_gid_allowed_for_policy (uid,
num_groups,
groups,
policy,
resource,
result);
out:
g_free (username);
g_free (groups);
return r;
}
#ifndef HAVE_GETGROUPLIST
/* Get group list for the named user.
* Return up to ngroups in the groups array.
* Return actual number of groups in ngroups.
* Return -1 if more groups found than requested.
*/
int
getgrouplist (const char *name, int baseid, int *groups, int *ngroups)
{
struct group *g;
int n = 0;
int i;
int ret;
if (*ngroups <= 0) {
return (-1);
}
*groups++ = baseid;
n++;
setgrent ();
while ((g = getgrent ()) != NULL) {
for (i = 0; g->gr_mem[i]; i++) {
if (strcmp (name, g->gr_mem[0]) == 0) {
*groups++ = g->gr_gid;
if (++n > *ngroups) {
break;
}
}
}
}
endgrent ();
ret = (n > *ngroups) ? -1 : n;
*ngroups = n;
return (ret);
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1