+2011/09/10 yocto <yocto@users.sourceforge.jp>
+
+ 0.4 Released
+
+ * The version of ccstools used was changed from 1.8.1 to 1.8.2p4.
+
+ * Support policy namespace.
+
+ * Fix several bugs.
+
+
2011/06/18 yocto <yocto@users.sourceforge.jp>
0.3 Released
+2011/09/10 yocto <yocto@users.sourceforge.jp>
+
+ 0.4 Released
+
+ * ベースとなるccs-editpolicyをccstools 1.8.2p4 20110820版 に変更
+
+ * ポリシー名前空間対応
+
+ * バグ修正
+
+
2011/06/18 yocto <yocto@users.sourceforge.jp>
0.3 Released
-gpet 0.3 2011-06-18
+gpet 0.4 2011-09-10
gpet (Gui Policy Editor for TOMOYO Linux)
Build / Install / Run / Uninstall
$ sudo apt-get install intltool libgtk2.0-dev libgconf2-dev
-$ tar xjvf gpet-0.3.tar.bz2
+$ tar xjvf gpet-0.4.tar.bz2
-$ cd gpet-0.3
+$ cd gpet-0.4
$ ./configure --prefix /usr
$ make
dnl Process this file with autoconf to produce a configure script.
dnl Created by Anjuta application wizard.
-AC_INIT(gpet, 0.3)
+AC_INIT(gpet, 0.4)
__GPET=gpet
AC_SUBST(__GPET)
msgstr ""
"Project-Id-Version: 0.1\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2011-06-10 13:56+0900\n"
-"PO-Revision-Date: 2011-06-10 13:56+0900\n"
+"POT-Creation-Date: 2011-09-05 17:05+0900\n"
+"PO-Revision-Date: 2011-09-05 17:05+0900\n"
"Last-Translator: Yoshihiro Kusuno <yocto@users.sourceforge.jp>\n"
"Language-Team: Japanese < >\n"
"Language: \n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: ../src/gpet.c:1193
+#: ../src/gpet.c:1239
msgid "offline"
msgstr "オフライン"
-#: ../src/gpet.c:1193
+#: ../src/gpet.c:1239
msgid "nework"
msgstr "ネットワーク"
-#: ../src/gpet.c:1193
+#: ../src/gpet.c:1239
msgid "online"
msgstr "オンライン"
-#: ../src/gpet.c:1237 ../src/gpet.c:1242 ../src/menu.c:79
+#: ../src/gpet.c:1284 ../src/gpet.c:1289 ../src/menu.c:79
msgid "Statistics"
msgstr "統計情報"
-#: ../src/gpet.c:1248 ../src/gpet.c:1252 ../src/gpet.c:1257
+#: ../src/gpet.c:1295 ../src/gpet.c:1299 ../src/gpet.c:1304
msgid "Manager Policy"
msgstr "マネージャ 一覧"
-#: ../src/gpet.c:1263 ../src/gpet.c:1267 ../src/gpet.c:1272
+#: ../src/gpet.c:1310 ../src/gpet.c:1314 ../src/gpet.c:1319
msgid "Domain Policy Editor"
msgstr "ドメインポリシーエディタ"
-#: ../src/gpet.c:1443 ../src/gpet.c:1468
+#: ../src/gpet.c:1492 ../src/gpet.c:1517
msgid "gpet"
msgstr "gpet"
#. create tab
-#: ../src/gpet.c:1575 ../src/menu.c:469
+#: ../src/gpet.c:1642 ../src/menu.c:473
msgid "Domain Transition"
msgstr "ドメイン遷移 一覧"
-#: ../src/gpet.c:1576
+#: ../src/gpet.c:1643
msgid "Exception Policy"
msgstr "例外ポリシー 一覧"
-#: ../src/gpet.c:1577
+#: ../src/gpet.c:1644
msgid "Profile"
msgstr "プロファイル 一覧"
msgid "Detach ACL window"
msgstr "ドメインポリシーを別ウィンドウで表示します。"
-#: ../src/menu.c:466
+#: ../src/menu.c:470
msgid "Process State"
msgstr "プロセス 一覧"
-#: ../src/menu.c:748
+#: ../src/menu.c:774
#, c-format
msgid ""
"<span foreground='red' size='x-large'><b>Delete</b> the %d selected "
"<span foreground='red' size='x-large'>選択した %d行の例外ポリシーを<b>削除</"
"b>します。</span>"
-#: ../src/menu.c:751
+#: ../src/menu.c:777
#, c-format
msgid ""
"<span foreground='red' size='x-large'><b>Delete</b> the selected exception "
"<span foreground='red' size='x-large'>選択した 例外ポリシーを<b>削除</b>しま"
"す。</span>"
-#: ../src/menu.c:768
+#: ../src/menu.c:794
#, c-format
msgid ""
"<span foreground='red' size='x-large'><b>Delete</b> the %d selected domains?"
"<span foreground='red' size='x-large'>選択した %d行のドメインを<b>削除</b>し"
"ます。</span>"
-#: ../src/menu.c:771
+#: ../src/menu.c:797
#, c-format
msgid ""
"<span foreground='red' size='x-large'><b>Delete</b> the selected domain?</"
"<span foreground='red' size='x-large'>選択した ドメインを<b>削除</b>します。"
"</span>"
-#: ../src/menu.c:790
+#: ../src/menu.c:816
#, c-format
msgid ""
"<span foreground='blue' size='x-large'><b>Delete</b> the %d selected "
"<span foreground='red' size='x-large'>選択した %d行のポリシーを<b>削除</b>し"
"ます。</span>"
-#: ../src/menu.c:793
+#: ../src/menu.c:819
#, c-format
msgid ""
"<span foreground='blue' size='x-large'><b>Delete</b> the selected policy?</"
"<span foreground='red' size='x-large'>選択した ポリシーを<b>削除</b>します。"
"</span>"
-#: ../src/menu.c:1027
+#: ../src/menu.c:1053
msgid "Add Domain"
msgstr "ドメインを追加します。"
-#: ../src/menu.c:1034
+#: ../src/menu.c:1060
msgid "Add Acl"
msgstr "ドメインに対するアクセス許可を追加します。"
-#: ../src/menu.c:1046
+#: ../src/menu.c:1072
msgid "Add Exception"
msgstr "例外ポリシーを追加します。"
-#: ../src/menu.c:1054
+#: ../src/menu.c:1080
msgid "Add Profile (0 - 255)"
msgstr "プロファイルを追加します。 (0 - 255)"
-#: ../src/menu.c:1196
+#: ../src/menu.c:1088
+msgid "Add Namespace"
+msgstr "ポリシー名前空間を追加します。"
+
+#: ../src/menu.c:1230
msgid "Profile list"
msgstr "プロファイルを選択します。"
-#: ../src/menu.c:1271
+#: ../src/menu.c:1305
msgid "Profile Edit"
msgstr "プロファイルを変更します。"
-#: ../src/menu.c:1355
+#: ../src/menu.c:1389
msgid "Yoshihiro Kusuno <yocto@users.sourceforge.jp>"
-msgstr "楠野 佳宏 <yocto@users.sourceforge.jp>"
+msgstr "クスノ <yocto@users.sourceforge.jp>"
-#: ../src/menu.c:1356
+#: ../src/menu.c:1390
msgid "ccstools --- kumaneko san"
msgstr "ccstools --- 熊猫さん"
-#: ../src/menu.c:1358
+#: ../src/menu.c:1392
msgid "Yoshihiro Kusuno"
-msgstr "楠野 佳宏"
+msgstr "クスノ"
#: ../src/other.c:49
msgid "Edit the selected line"
}
/*---------------------------------------------------------------------------*/
+static gboolean is_jump_source(
+ struct ccs_domain_policy3 *dp, const int index)
+{
+ return dp->list[index].target != NULL;
+}
+/*---------------------------------------------------------------------------*/
enum tree_column_pos {
COLUMN_INDEX, // data index (invisible)
COLUMN_NUMBER, // n
};
/*---------------------------------------------------------------------------*/
static int add_tree_store(GtkTreeStore *store, GtkTreeIter *parent_iter,
- struct ccs_domain_policy *dp, int *index, int nest)
+ struct ccs_domain_policy3 *dp, int *index, int nest)
{
GtkTreeIter iter;
gchar *color = "black";
//g_print("add_tree_store index[%3d] nest[%2d]\n", *index, nest);
- sp = ccs_domain_name(dp, *index);
+ sp = get_domain_name(dp, *index);
for (n = 0; ; n++) {
const char *cp = strchr(sp, ' ');
if (!cp)
number = dp->list[*index].number;
if (number >= 0) {
str_num = g_strdup_printf("%4d", number);
- str_prof = dp->list[*index].profile_assigned ?
- g_strdup_printf("%3u", dp->list[*index].profile) :
- g_strdup("???");
+ str_prof = g_strdup_printf("%3u", dp->list[*index].profile);
} else {
str_num = g_strdup("");
str_prof = g_strdup("");
COLUMN_COLON, number >= 0 ? ":" : "",
COLUMN_PROFILE, str_prof,
COLUMN_KEEPER_DOMAIN, dp->list[*index].is_dk ? "#" : " ",
- COLUMN_INITIALIZER_TARGET, dp->list[*index].is_dit ? "*" : " ",
+ COLUMN_INITIALIZER_TARGET, dp->list[*index].is_djt ? "*" : " ",
COLUMN_DOMAIN_UNREACHABLE, dp->list[*index].is_du ? "!" : " ",
-1);
g_free(str_num);
g_free(str_prof);
transition_control = dp->list[*index].d_t;
- if (transition_control && !(dp->list[*index].is_dis)) {
+ if (transition_control && !is_jump_source(dp, *index)) {
line = g_strdup_printf(" ( %s%s from %s )",
get_transition_name(transition_control->type),
transition_control->program ?
transition_control->domainname ?
transition_control->domainname->name : "any");
color =
- transition_control->type == CCS_TRANSITION_CONTROL_KEEP ?
- "green" : "cyan";
- } else if (dp->list[*index].is_dis) { /* initialize_domain */
- is_dis = g_strdup_printf(CCS_ROOT_NAME "%s",
- strrchr(ccs_domain_name(dp, *index), ' '));
- redirect_index = ccs_find_domain(dp, is_dis, false, false);
- g_free(is_dis);
+ transition_control->type == CCS_TRANSITION_CONTROL_KEEP ?
+ "green" : "cyan";
+ } else if (is_jump_source(dp, *index)) { /* initialize_domain */
+ redirect_index = get_find_target_domain(*index);
color = "blue";
if (redirect_index >= 0)
is_dis = g_strdup_printf(" ( -> %d )",
dp->list[redirect_index].number);
- else
+ else if (redirect_index == EOF)
is_dis = g_strdup_printf(" ( -> Not Found )");
+ else
+ is_dis = g_strdup_printf(" ( -> Namespace jump )");
} else if (dp->list[*index].is_dd) { /* delete_domain */
color = "gray";
}
- domain = g_strdup_printf("%s%s%s%s%s",
+ domain = g_strdup_printf("%s%s%s%s%s%s",
dp->list[*index].is_dd ? "( " : "",
+ is_jump_source(dp, *index) ? "=> " : "",
name,
dp->list[*index].is_dd ? " )" : "",
line ? line : "",
(*index)++;
while (*index < dp->list_len) {
- sp = ccs_domain_name(dp, *index);
+ sp = get_domain_name(dp, *index);
for (n = 0; ; n++) {
const char *cp = strchr(sp, ' ');
if (!cp)
return n;
}
-void add_tree_data(GtkTreeView *treeview, struct ccs_domain_policy *dp)
+void add_tree_data(GtkTreeView *treeview, struct ccs_domain_policy3 *dp)
{
GtkTreeStore *store;
GtkTreeIter *iter = NULL;
store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
gtk_tree_store_clear(store); // TODO 遅い
- add_tree_store(store, iter, dp, &index, nest);
+ if (dp->list_len > 0)
+ add_tree_store(store, iter, dp, &index, nest);
}
/*---------------------------------------------------------------------------*/
static GtkTreeViewColumn *column_add(
N_COLUMNS_LIST
};
-void add_list_data(generic_list_t *generic, gboolean alias_flag)
+void add_list_data(generic_list_t *generic,
+ enum ccs_screen_type current_page)
{
GtkListStore *store;
GtkTreeIter iter;
int i;
- gchar *str_num, *profile, *alias;
+ gchar *str_num, *ope, *profile, *alias;
store = GTK_LIST_STORE(gtk_tree_view_get_model(
GTK_TREE_VIEW(generic->listview)));
str_num = g_strdup_printf("%4d", i);
gtk_list_store_append(store, &iter);
- if (alias_flag) {
- gchar *ope = decode_from_octal_str(
- generic->list[i].operand);
-
+ switch((int)current_page) {
+ case CCS_SCREEN_EXCEPTION_LIST :
+ case CCS_SCREEN_ACL_LIST :
+ ope = decode_from_octal_str(generic->list[i].operand);
alias = (gchar *)
ccs_directives[generic->list[i].directive].alias;
gtk_list_store_set(store, &iter,
- LIST_NUMBER, str_num,
- LIST_COLON, ":",
- LIST_ALIAS, alias,
-// LIST_OPERAND, generic->list[i].operand,
- LIST_OPERAND, ope, // test
- -1);
- g_free(ope); // test
- } else {
+ LIST_NUMBER, str_num,
+ LIST_COLON, ":",
+ LIST_ALIAS, alias,
+ LIST_OPERAND, ope,
+ -1);
+ g_free(ope);
+ break;
+ case CCS_SCREEN_PROFILE_LIST :
profile = g_strdup_printf("%3u-",
generic->list[i].directive);
alias = g_strdup_printf("%s%s",
-1);
g_free(profile);
g_free(alias);
+ break;
+ case CCS_SCREEN_NS_LIST :
+ gtk_list_store_set(store, &iter,
+ LIST_NUMBER, str_num,
+ LIST_COLON, ":",
+ LIST_OPERAND, generic->list[i].operand,
+ -1);
+ break;
}
g_free(str_num);
}
view = transition->treeview;
gtk_tree_model_get(model, iter, COLUMN_INDEX, &index, -1);
str_buff = decode_from_octal_str(
- ccs_domain_name(transition->dp, index));
+ get_domain_name(transition->dp, index));
cmp = strcmp(entry, str_buff);
break;
case ADDENTRY_ACL_LIST :
cmp = atoi(entry) - transition->prf.list[atoi(str_buff)].directive;
//g_print("entry[%s] [%s:%d]\n", entry, str_buff, transition->prf.list[atoi(str_buff)].directive);
break;
+ case ADDENTRY_NAMESPACE_LIST :
+ view = transition->ns.listview;
+ gtk_tree_model_get(model, iter, LIST_OPERAND, &operand, -1);
+ cmp = strcmp(entry, operand);
+ break;
}
g_free(alias);
g_free(operand);
case ADDENTRY_PROFILE_LIST :
view = transition->prf.listview;
break;
+ case ADDENTRY_NAMESPACE_LIST :
+ view = transition->ns.listview;
+ break;
}
model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
g_list_free(list);
gtk_tree_model_get(model, &iter, COLUMN_INDEX, &index, -1);
DEBUG_PRINT("--- index [%4d] ---\n", index);
- name = decode_from_octal_str(ccs_domain_name(transition->dp, index));
+ name = decode_from_octal_str(get_domain_name(transition->dp, index));
+ if (is_jump_source(transition->dp, index)) {
+ gchar *cp = strrchr(name, ' ');
+ if (cp)
+ *cp = '\0';
+ }
gtk_entry_set_text(GTK_ENTRY(transition->domainbar), name);
g_free(name);
get_acl_list(transition->dp, index,
&(transition->acl.list), &(transition->acl.count));
- add_list_data(&(transition->acl), TRUE);
+ add_list_data(&(transition->acl), CCS_SCREEN_ACL_LIST);
if (transition->acl.count) {
set_position_addentry(transition, &path);
}
DEBUG_PRINT("Out **************************** \n");
}
-#if 0
-static void cb_selection (GtkTreeView *treeview,
- transition_t *transition)
+/*---------------------------------------------------------------------------*/
+static void set_ns_tab_label(transition_t *transition,
+ const gchar *namespace)
{
- GtkTreeSelection *selection;
- GtkTreeIter iter;
- gint select_count;
- GtkTreeModel *model;
- GList *list;
- gint index;
- GtkTreePath *path = NULL;
- GtkTreeViewColumn *column = NULL;
-
- DEBUG_PRINT("In **************************** \n");
- selection = gtk_tree_view_get_selection(treeview);
- select_count = gtk_tree_selection_count_selected_rows(selection);
- DEBUG_PRINT("select count[%d]\n", select_count);
- if (0 == select_count)
- return;
-
- model = gtk_tree_view_get_model(
- GTK_TREE_VIEW(transition->treeview));
- list = gtk_tree_selection_get_selected_rows(selection, NULL);
- gtk_tree_model_get_iter(model, &iter, g_list_first(list)->data);
- g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL);
- g_list_free(list);
- gtk_tree_model_get(model, &iter, COLUMN_INDEX, &index, -1);
- DEBUG_PRINT("--- index [%4d] ---\n", index);
- gtk_entry_set_text(GTK_ENTRY(transition->domainbar),
- ccs_domain_name(transition->dp, index));
-
- gtk_tree_view_get_cursor(GTK_TREE_VIEW(
- transition->acl.listview), &path, &column);
-
- get_acl_list(transition->dp, index,
- &(transition->acl.list), &(transition->acl.count));
- add_list_data(&(transition->acl), TRUE);
-
- if (transition->acl.count) {
- DEBUG_PRINT("ACL count<%d>\n", transition->acl.count);
- DEBUG_PRINT("ACL ");
- view_cursor_set(transition->acl.listview, path, column);
- disp_statusbar(transition, CCS_SCREEN_ACL_LIST);
- } else { /* delete_domain or initializer_source */
- disp_statusbar(transition, CCS_MAXSCREEN);
- }
- DEBUG_PRINT("Out **************************** \n");
+ GtkWidget *notebook;
+ GtkWidget *tab4;
+
+ notebook = g_object_get_data(
+ G_OBJECT(transition->window), "notebook");
+ tab4 = g_object_get_data(
+ G_OBJECT(transition->window), "tab4");
+ gtk_notebook_set_tab_label_text(
+ GTK_NOTEBOOK(notebook), tab4, namespace);
+ gtk_notebook_set_menu_label_text(
+ GTK_NOTEBOOK(notebook), tab4, namespace);
+
+ put_ns_name(namespace);
}
-#endif
/*---------------------------------------------------------------------------*/
struct FindIsDis_t {
gint redirect_index;
}
static void cb_initialize_domain(GtkTreeView *treeview, GtkTreePath *treepath,
- GtkTreeViewColumn treeviewcolumn, gpointer dummy)
+ GtkTreeViewColumn *treeviewcolumn, transition_t *transition)
{
- GtkTreeIter iter;
- GtkTreeModel *model;
- gboolean ret;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
struct FindIsDis_t data;
+ gint index;
DEBUG_PRINT("In **************************** \n");
model = gtk_tree_view_get_model(treeview);
- ret = gtk_tree_model_get_iter(model, &iter, treepath);
- if (ret)
- gtk_tree_model_get(model, &iter,
+ if (!gtk_tree_model_get_iter(model, &iter, treepath))
+ return;
+
+ gtk_tree_model_get(model, &iter, COLUMN_INDEX, &index,
COLUMN_REDIRECT, &data.redirect_index, -1);
- DEBUG_PRINT("redirect_index[%d]\n", data.redirect_index);
- if (!ret || data.redirect_index < 0)
+//#undef DEBUG_PRINT
+//#define DEBUG_PRINT g_print
+ DEBUG_PRINT("index[%d] redirect_index[%d]\n", index, data.redirect_index);
+
+ if (data.redirect_index == -2) {
+ gchar *namespace =
+ g_strdup(transition->dp->list[index].target->name);
+ DEBUG_PRINT("%s\n", namespace);
+ char *cp = strchr(namespace, ' ');
+ if (cp)
+ *cp = '\0';
+ set_ns_tab_label(transition, namespace);
+ g_free(namespace);
+ refresh_transition(NULL, transition);
+ return;
+ } else if (data.redirect_index < 0)
return; /* not initialize_domain */
data.path = NULL;
sens_add = TRUE;
sens_cpy = TRUE;
break;
+ case CCS_SCREEN_NS_LIST :
+ sens_add = TRUE;
+ sens_cpy = TRUE;
+ break;
}
gtk_action_set_sensitive(gtk_action_group_get_action(
transition->current_page);
return popup_menu(transition, event->button);
}
+
+static gboolean cb_select_ns(GtkTreeView *listview, GdkEventButton *event,
+ transition_t *transition)
+{
+ transition->current_page = CCS_SCREEN_NS_LIST;
+ set_sensitive(transition->actions, transition->task_flag,
+ transition->current_page);
+ return popup_menu(transition, event->button);
+}
+/*---------------------------------------------------------------------------*/
+static void cb_ns_selection(GtkTreeSelection *selection,
+ transition_t *transition)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GList *list;
+ gchar *namespace;
+
+ if (0 == gtk_tree_selection_count_selected_rows(selection))
+ return;
+
+ model = gtk_tree_view_get_model(
+ GTK_TREE_VIEW(transition->ns.listview));
+ list = gtk_tree_selection_get_selected_rows(selection, NULL);
+ gtk_tree_model_get_iter(model, &iter, g_list_first(list)->data);
+ g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free(list);
+ gtk_tree_model_get(model, &iter, LIST_OPERAND, &namespace, -1);
+ set_ns_tab_label(transition, namespace);
+ g_free(namespace);
+}
/*---------------------------------------------------------------------------*/
static gboolean inc_search(GtkTreeModel *model, gint column,
const gchar *key, GtkTreeIter *iter, gpointer search_data)
GtkTreeModel *model;
GtkTreeIter iter;
gint index;
- struct ccs_domain_policy *dp = transition->dp;
+ struct ccs_domain_policy3 *dp = transition->dp;
model = gtk_tree_view_get_model(
DEBUG_PRINT("index[%d]\n", index);
/* deleted_domain or initializer_source */
- if (!(dp->list[index].is_dd) && !(dp->list[index].is_dis))
+ if (!(dp->list[index].is_dd) && !(dp->list[index].target))
dp->list_selected[index] = 1;
}
return result;
}
/*---------------------------------------------------------------------------*/
-static void create_tabs(GtkWidget *notebook, GtkWidget *box, gchar *str)
+static void create_tabs(GtkWidget *notebook, GtkWidget *box, const gchar *str)
{
GtkWidget *label, *label_box, *menu_box;
CCS_PROC_POLICY_EXCEPTION_POLICY);
break;
case CCS_SCREEN_PROFILE_LIST :
+ case CCS_SCREEN_NS_LIST :
if (is_offline())
title = g_strdup_printf("%s - %s%s%s", mode[e_off], dir,
CCS_DISK_POLICY_DIR, CCS_DISK_POLICY_PROFILE);
GTK_TREE_VIEW(tran->treeview));
selection_list = gtk_tree_view_get_selection(
GTK_TREE_VIEW(tran->acl.listview));
-
if (tran->acl_detached) {
- if(selection_list &&
- gtk_tree_selection_count_selected_rows(selection_list))
- tran->current_page = CCS_SCREEN_ACL_LIST;
+ tran->current_page = CCS_SCREEN_ACL_LIST;
control_acl_window(tran);
- g_object_set(G_OBJECT(notebook), "can-focus", FALSE, NULL);
- } else {
- if (tran->task_flag ||
- (selection_tree &&
- gtk_tree_selection_count_selected_rows(selection_tree)))
- tran->current_page = CCS_SCREEN_DOMAIN_LIST;
- else if(selection_list &&
- gtk_tree_selection_count_selected_rows(selection_list))
- tran->current_page = CCS_SCREEN_ACL_LIST;
- else
- tran->current_page = CCS_MAXSCREEN;
- g_object_set(G_OBJECT(notebook), "can-focus", TRUE, NULL);
}
+ if (tran->task_flag ||
+ (selection_tree &&
+ gtk_tree_selection_count_selected_rows(selection_tree)))
+ tran->current_page = CCS_SCREEN_DOMAIN_LIST;
+ else if(selection_list &&
+ gtk_tree_selection_count_selected_rows(selection_list))
+ tran->current_page = CCS_SCREEN_ACL_LIST;
+ else
+ tran->current_page = CCS_MAXSCREEN;
+ g_object_set(G_OBJECT(notebook), "can-focus", TRUE, NULL);
break;
case 1 :
tran->current_page = CCS_SCREEN_EXCEPTION_LIST;
}
g_object_set(G_OBJECT(notebook), "can-focus", FALSE, NULL);
break;
+ case 3 :
+ tran->current_page = CCS_SCREEN_NS_LIST;
+ if (tran->acl_detached) {
+ control_acl_window(tran);
+ }
+ g_object_set(G_OBJECT(notebook), "can-focus", FALSE, NULL);
+ break;
}
title = disp_window_title(tran->current_page);
gtk_window_set_title(GTK_WINDOW(tran->window), title);
g_free(title);
-// disp_statusbar(tran, tran->current_page);
set_sensitive(tran->actions, tran->task_flag,
tran->current_page);
refresh_transition(NULL, tran);
+ disp_statusbar(tran, tran->current_page);
DEBUG_PRINT("Out Tab[%d]\n", page_num);
}
/*---------------------------------------------------------------------------*/
GtkWidget *statusbar;
gint contextid;
GtkWidget *vbox;
- GtkWidget *tab1, *tab2, *tab3;
+ GtkWidget *tab1, *tab2, *tab3, *tab4;
GtkWidget *notebook;
GtkWidget *pane;
GtkWidget *domainbar;
GtkWidget *treeview, *listview;
GtkContainer *container, *acl_container;
gchar *title;
- struct ccs_domain_policy dp = { NULL, 0, NULL };
+ struct ccs_domain_policy3 dp = { NULL, 0, NULL };
transition_t transition;
transition.task_flag = 0;
// cursor move domain window
g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)),
- "changed", G_CALLBACK(cb_selection), &transition);
+ "changed", G_CALLBACK(cb_selection), &transition);
// g_signal_connect(GTK_TREE_VIEW(treeview), "cursor-changed",
// G_CALLBACK(cb_selection), &transition);
// double click or enter key domain window
g_signal_connect(G_OBJECT(treeview), "row-activated",
- G_CALLBACK(cb_initialize_domain), NULL);
+ G_CALLBACK(cb_initialize_domain), &transition);
// mouse click domain window
g_signal_connect(G_OBJECT(treeview), "button-press-event",
G_CALLBACK(cb_select_domain), &transition);
if (get_profile(&(transition.prf.list), &(transition.prf.count)))
g_warning("Read error : profile");
else
- add_list_data(&(transition.prf), FALSE);
+ add_list_data(&(transition.prf), CCS_SCREEN_PROFILE_LIST);
// mouse click profile window
g_signal_connect(G_OBJECT(listview), "button-press-event",
G_CALLBACK(cb_select_prf), &transition);
+ tab4 = gtk_vbox_new(FALSE, 1);
+ // create namespace view
+ listview = create_list_model(FALSE);
+ create_list_view(tab4, listview, FALSE);
+ transition.ns.listview = listview;
+ transition.ns.count = 0;
+ transition.ns.list = NULL;
+ if (get_namespace(&(transition.ns.list), &(transition.ns.count)))
+ g_warning("Read error : namespace");
+ else
+ add_list_data(&(transition.ns), CCS_SCREEN_NS_LIST);
+ // mouse click namespace window
+ g_signal_connect(G_OBJECT(listview), "button-press-event",
+ G_CALLBACK(cb_select_ns), &transition);
+ // cursor move namespace window
+ g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(listview)),
+ "changed", G_CALLBACK(cb_ns_selection), &transition);
+
// create tab
create_tabs(notebook, tab1, _("Domain Transition"));
create_tabs(notebook, tab2, _("Exception Policy"));
create_tabs(notebook, tab3, _("Profile"));
+ create_tabs(notebook, tab4, get_ns_name());
/* to save menu.c Process_state() */
g_object_set_data(G_OBJECT(window), "notebook", notebook);
g_object_set_data(G_OBJECT(window), "tab1", tab1);
+ /* to save cb_ns_selection() */
+ g_object_set_data(G_OBJECT(window), "tab4", tab4);
// tab change
g_signal_connect(G_OBJECT(notebook), "switch_page",
ADDENTRY_EXCEPTION_LIST,
ADDENTRY_PROFILE_LIST,
ADDENTRY_MANAGER_LIST,
+ ADDENTRY_NAMESPACE_LIST,
ADDENTRY_NON
};
GtkContainer *container;
- struct ccs_domain_policy *dp;
+ struct ccs_domain_policy3 *dp;
int domain_count;
generic_list_t acl; // ACL
GtkWidget *acl_window;
gboolean acl_detached;
generic_list_t exp; // exception
generic_list_t prf; // profile
+ generic_list_t ns; // namespace
task_list_t tsk; // Process
int task_flag; // 1:process
// 0:domain
int ccs_main(int argc, char *argv[]);
// interface.inc
-int get_domain_policy(struct ccs_domain_policy *dp, int *count);
+int get_domain_policy(struct ccs_domain_policy3 *dp, int *count);
int add_domain(char *input, char **err_buff);
-int set_profile(struct ccs_domain_policy *dp,
+int set_profile(struct ccs_domain_policy3 *dp,
char *profile, char **err_buff);
int get_task_list(struct ccs_task_entry **tsk, int *count);
-int get_acl_list(struct ccs_domain_policy *dp, int current,
+const char *get_domain_name(const struct ccs_domain_policy3 *dp,
+ const int index);
+int get_acl_list(struct ccs_domain_policy3 *dp, int current,
struct ccs_generic_acl **ga, int *count);
int get_process_acl_list(int current,
struct ccs_generic_acl **ga, int *count);
int get_optimize_acl_list(int current, struct ccs_generic_acl **ga, int count);
-int add_acl_list(struct ccs_domain_policy *dp, int current,
+int add_acl_list(struct ccs_domain_policy3 *dp, int current,
char *input, char **err_buff);
const char *get_transition_name(enum ccs_transition_type type);
int get_exception_policy(struct ccs_generic_acl **ga, int *count);
int get_profile(struct ccs_generic_acl **ga, int *count);
int add_profile(char *input, char **err_buff);
int set_profile_level(int index, const char *input, char **err_buff);
+int get_namespace(struct ccs_generic_acl **ga, int *count);
+int add_namespace(char *input, char **err_buff);
int get_manager(struct ccs_generic_acl **ga, int *count);
int add_manager(char *input, char **err_buff);
int get_memory(struct ccs_generic_acl **ga, int *count);
int set_memory(struct ccs_generic_acl *ga, int count, char **err_buff);
-int delete_domain_policy(struct ccs_domain_policy *dp, char **err_buff);
-int delete_acl_policy(struct ccs_domain_policy *dp, char **err_buff,
+int delete_domain_policy(struct ccs_domain_policy3 *dp, char **err_buff);
+int delete_acl_policy(struct ccs_domain_policy3 *dp, char **err_buff,
struct ccs_generic_acl *ga, int count);
-int delete_exp_policy(struct ccs_domain_policy *dp, char **err_buff,
+int delete_exp_policy(struct ccs_domain_policy3 *dp, char **err_buff,
struct ccs_generic_acl *ga, int count);
int delete_manager_policy(
struct ccs_generic_acl *ga, int count, char **err_buff);
char *get_remote_ip(char *str_ip);
const char *get_policy_dir(void);
const char *get_domain_last_name(const int index);
+int get_find_target_domain(const int index);
+const char *get_ns_name(void);
+void put_ns_name(const char *namespace);
// gpet.c
gchar *decode_from_octal_str(const char *name);
-void add_tree_data(GtkTreeView *treeview, struct ccs_domain_policy *dp);
-void add_list_data(generic_list_t *generic, gboolean alias_flag);
+void add_tree_data(GtkTreeView *treeview, struct ccs_domain_policy3 *dp);
+void add_list_data(generic_list_t *generic,
+ enum ccs_screen_type current_page);
gint get_current_domain_index(transition_t *transition);
gchar *get_alias_and_operand(GtkWidget *view, gboolean alias_flag);
void set_position_addentry(transition_t *transition, GtkTreePath **path);
void write_config(transition_t *tran);
// other.c
+gboolean namespace_main(transition_t *transition);
void manager_main(transition_t *transition);
void memory_main(transition_t *transition);
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA
*/
+static void ccs_out_of_memory(void)
+{
+ fprintf(stderr, "Out of memory. Aborted.\n");
+ exit(1);
+}
-int get_domain_policy(struct ccs_domain_policy *dp, int *count)
+int get_domain_policy(struct ccs_domain_policy3 *dp, int *count)
{
int result = 1;
return result;
}
-int set_profile(struct ccs_domain_policy *dp,
+int set_profile(struct ccs_domain_policy3 *dp,
char *profile, char **err_buff)
{
int dummy = 0, result = 0;
}
}
-int get_acl_list(struct ccs_domain_policy *dp, int current,
+const char *get_domain_name(const struct ccs_domain_policy3 *dp,
+ const int index)
+{
+ return dp->list[index].domainname->name;
+}
+
+int get_acl_list(struct ccs_domain_policy3 *dp, int current,
struct ccs_generic_acl **ga, int *count)
{
int result = 0;
ccs_dp = *dp;
- if (ccs_initializer_source(current)) {
+ if (ccs_jump_source(current)) {
*count = 0;
} else if (ccs_deleted_domain(current)) {
*count = 0;
} else {
free(ccs_current_domain);
- ccs_current_domain = strdup(ccs_domain_name(dp, current));
+ ccs_current_domain = strdup(get_domain_name(dp, current));
if (!ccs_current_domain)
ccs_out_of_memory();
else {
return 0;
}
-int add_acl_list(struct ccs_domain_policy *dp, int current,
+int add_acl_list(struct ccs_domain_policy3 *dp, int current,
char *input, char **err_buff)
{
int result = 0;
ccs_dp = *dp;
- if (ccs_initializer_source(current) ||
- ccs_deleted_domain(current)) {
+ if (ccs_jump_source(current) || ccs_deleted_domain(current)) {
*dp = ccs_dp;
return 1;
}
return result;
}
+int get_namespace(struct ccs_generic_acl **ga, int *count)
+{
+ struct ccs_generic_acl *orig_generic_acl_list = NULL;
+ int orig_generic_acl_list_count = 0;
+ int result;
+
+ generic_acl_copy(&ccs_gacl_list,
+ ccs_gacl_list_count,
+ &orig_generic_acl_list,
+ orig_generic_acl_list_count);
+ orig_generic_acl_list_count = ccs_gacl_list_count;
+
+ ccs_current_screen = CCS_SCREEN_NS_LIST;
+ result = ccs_generic_list_loop();
+
+ generic_acl_copy(&ccs_gacl_list,
+ ccs_gacl_list_count,
+ ga, *count);
+ *count = ccs_gacl_list_count;
+
+ generic_acl_copy(&orig_generic_acl_list,
+ orig_generic_acl_list_count,
+ &ccs_gacl_list,
+ ccs_gacl_list_count);
+ ccs_gacl_list_count = orig_generic_acl_list_count;
+
+ return result;
+}
+
+int add_namespace(char *input, char **err_buff)
+{
+ int result = 0;
+
+ ccs_current_screen = CCS_SCREEN_NS_LIST;
+ gpet_line = input;
+ ccs_add_entry();
+ gpet_line = NULL;
+
+ if (ccs_last_error) {
+ (*err_buff) = strdup(ccs_last_error);
+ free(ccs_last_error);
+ ccs_last_error = NULL;
+ result = 1;
+ }
+
+ return result;
+}
+
int get_manager(struct ccs_generic_acl **ga, int *count)
{
struct ccs_generic_acl *orig_generic_acl_list = NULL;
return result;
}
-int delete_domain_policy(struct ccs_domain_policy *dp, char **err_buff)
+int delete_domain_policy(struct ccs_domain_policy3 *dp, char **err_buff)
{
int result = 0;
return result;
}
-int delete_acl_policy(struct ccs_domain_policy *dp, char **err_buff,
+int delete_acl_policy(struct ccs_domain_policy3 *dp, char **err_buff,
struct ccs_generic_acl *ga, int count)
{
int result = 0;
return result;
}
-int delete_exp_policy(struct ccs_domain_policy *dp, char **err_buff,
+int delete_exp_policy(struct ccs_domain_policy3 *dp, char **err_buff,
struct ccs_generic_acl *ga, int count)
{
int result = 0;
{
return ccs_get_last_name(index);
}
+
+int get_find_target_domain(const int index)
+{
+ return ccs_find_target_domain(index);
+}
+
+const char *get_ns_name(void)
+{
+ return ccs_current_ns->name;
+}
+
+void put_ns_name(const char *namespace)
+{
+ ccs_current_ns = ccs_savename(namespace);
+}
status_str = g_strdup_printf("Entry[%d]",
transition->prf.count);
break;
+ case CCS_SCREEN_NS_LIST :
+ status_str = g_strdup_printf("Entry[%d]",
+ transition->ns.count);
+ break;
case CCS_SCREEN_DOMAIN_LIST :
case CCS_SCREEN_ACL_LIST :
if (transition->task_flag)
if (!path) {
path = gtk_tree_path_new_from_indices(0, -1);
}
-
+#if 0
{
gchar *path_str = gtk_tree_path_to_string(path);
DEBUG_PRINT("<%s> TreePath[%s]\n",
g_type_name(G_OBJECT_TYPE(view)), path_str);
g_free(path_str);
}
- gtk_tree_view_set_cursor(
- GTK_TREE_VIEW(view), path, column, FALSE);
+#endif
+ gtk_tree_view_set_cursor(GTK_TREE_VIEW(view), path, column, FALSE);
+ gtk_tree_path_free(path);
+ gtk_tree_view_get_cursor(GTK_TREE_VIEW(view), &path, &column);
+ if (!path) {
+ path = gtk_tree_path_new_from_indices(0, -1);
+ gtk_tree_view_set_cursor(
+ GTK_TREE_VIEW(view), path, column, FALSE);
+ }
gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(view), path, NULL,
TRUE, 0.5, 0.0); // ksn
gtk_tree_path_free(path);
}
+static void clear_acl_view(transition_t *transition)
+{
+ gtk_entry_set_text(GTK_ENTRY(transition->domainbar), get_ns_name());
+ transition->acl.count = 0;
+ add_list_data(&(transition->acl), CCS_SCREEN_ACL_LIST);
+}
+
void refresh_transition(GtkAction *action, transition_t *transition)
{
GtkTreePath *path = NULL;
if (get_exception_policy(
&(transition->exp.list), &(transition->exp.count)))
break;
- add_list_data(&(transition->exp), TRUE);
+ add_list_data(&(transition->exp), CCS_SCREEN_EXCEPTION_LIST);
set_position_addentry(transition, &path);
disp_statusbar(transition, CCS_SCREEN_EXCEPTION_LIST);
view_cursor_set(view, path, column);
if (get_profile(
&(transition->prf.list), &(transition->prf.count)))
break;
- add_list_data(&(transition->prf), FALSE);
+ add_list_data(&(transition->prf), CCS_SCREEN_PROFILE_LIST);
set_position_addentry(transition, &path);
disp_statusbar(transition, CCS_SCREEN_PROFILE_LIST);
view_cursor_set(view, path, column);
gtk_widget_grab_focus(view);
break;
+ case CCS_SCREEN_NS_LIST :
+ view = transition->ns.listview;
+ gtk_tree_view_get_cursor(GTK_TREE_VIEW(view),
+ &path, &column);
+ if (get_namespace(
+ &(transition->ns.list), &(transition->ns.count)))
+ break;
+ add_list_data(&(transition->ns), CCS_SCREEN_NS_LIST);
+ set_position_addentry(transition, &path);
+ disp_statusbar(transition, CCS_SCREEN_NS_LIST);
+ view_cursor_set(view, path, column);
+ gtk_widget_grab_focus(view);
+ break;
case CCS_SCREEN_DOMAIN_LIST :
case CCS_MAXSCREEN :
view = transition->task_flag ?
break;
add_task_tree_data(GTK_TREE_VIEW(view), &(transition->tsk));
gtk_tree_view_expand_all(GTK_TREE_VIEW(view));
+ view_cursor_set(view, path, column);
} else {
if (get_domain_policy(
transition->dp, &(transition->domain_count)))
add_tree_data(GTK_TREE_VIEW(view), transition->dp);
gtk_tree_view_expand_all(GTK_TREE_VIEW(view));
set_position_addentry(transition, &path);
+ if (transition->domain_count)
+ view_cursor_set(view, path, column);
+ else
+ clear_acl_view(transition);
}
-
- view_cursor_set(view, path, column);
gtk_widget_show(view);
-#if 0
- if (transition->acl_detached) { // get focus
- gtk_widget_grab_focus(transition->acl.listview);
- DEBUG_PRINT("☆Transition[%d] Acl[%d]\n",
- gtk_window_has_toplevel_focus(GTK_WINDOW(transition->window)),
- gtk_window_has_toplevel_focus(GTK_WINDOW(transition->acl_window)));
- } else {
- gtk_widget_grab_focus(view);
- }
-#endif
gtk_widget_grab_focus(view);
break;
case CCS_SCREEN_ACL_LIST :
view = transition->treeview;
if (index >= 0)
insert_history_buffer(view, decode_from_octal_str(
- ccs_domain_name(transition->dp, index)));
+ get_domain_name(transition->dp, index)));
break;
case CCS_SCREEN_ACL_LIST :
view = transition->acl.listview;
view = transition->prf.listview;
insert_history_buffer(view, get_alias_and_operand(view, FALSE));
break;
+ case CCS_SCREEN_NS_LIST :
+ view = transition->ns.listview;
+ insert_history_buffer(view, get_alias_and_operand(view, FALSE));
+ break;
default :
break;
}
result = add_profile(input, &err_buff);
type = ADDENTRY_PROFILE_LIST;
break;
+ case CCS_SCREEN_NS_LIST :
+ DEBUG_PRINT("append namespace\n");
+ result = append_dialog(transition,
+ _("Add Namespace"), &input);
+ if (!result)
+ result = add_namespace(input, &err_buff);
+ type = ADDENTRY_NAMESPACE_LIST;
+ break;
default :
DEBUG_PRINT("append ???\n");
break;
gtk_about_dialog_set_copyright(about,
"Copyright(C) 2010,2011 TOMOYO Linux Project");
gtk_about_dialog_set_comments(about,
- "Gui Policy Editor for TOMOYO Linux 1.8.1"
- " or AKARI 1.0.11"
+ "Gui Policy Editor for TOMOYO Linux 1.8.2"
+ " or AKARI 1.0.17"
"\n(based on ccs-editpolicy:ccstools)");
gtk_about_dialog_set_website(about, "http://sourceforge.jp/projects/gpet/");
// gtk_about_dialog_set_website_label(about, "http://tomoyo.sourceforge.jp/");
get_process_acl_list(index,
&(transition->acl.list), &(transition->acl.count));
- add_list_data(&(transition->acl), TRUE);
+ add_list_data(&(transition->acl), CCS_SCREEN_ACL_LIST);
view_cursor_set(transition->acl.listview, path, column);
*
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*
- * Version: 1.8.1 2011/04/01
+ * Version: 1.8.2+ 2011/08/20
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License v2 as published by the
*
* This function does not return.
*/
-void ccs_out_of_memory(void)
+static void ccs_out_of_memory(void)
{
fprintf(stderr, "Out of memory. Aborted.\n");
exit(1);
}
/**
+ * ccs_strdup - strdup() with abort on error.
+ *
+ * @string: String to duplicate.
+ *
+ * Returns copy of @string on success, abort otherwise.
+ */
+char *ccs_strdup(const char *string)
+{
+ char *cp = strdup(string);
+ if (!cp)
+ ccs_out_of_memory();
+ return cp;
+}
+
+/**
+ * ccs_realloc - realloc() with abort on error.
+ *
+ * @ptr: Pointer to void.
+ * @size: New size.
+ *
+ * Returns return value of realloc() on success, abort otherwise.
+ */
+void *ccs_realloc(void *ptr, const size_t size)
+{
+ void *vp = realloc(ptr, size);
+ if (!vp)
+ ccs_out_of_memory();
+ return vp;
+}
+
+/**
+ * ccs_realloc2 - realloc() with abort on error.
+ *
+ * @ptr: Pointer to void.
+ * @size: New size.
+ *
+ * Returns return value of realloc() on success, abort otherwise.
+ *
+ * Allocated memory is cleared with 0.
+ */
+void *ccs_realloc2(void *ptr, const size_t size)
+{
+ void *vp = ccs_realloc(ptr, size);
+ memset(vp, 0, size);
+ return vp;
+}
+
+/**
+ * ccs_malloc - malloc() with abort on error.
+ *
+ * @size: Size to allocate.
+ *
+ * Returns return value of malloc() on success, abort otherwise.
+ *
+ * Allocated memory is cleared with 0.
+ */
+void *ccs_malloc(const size_t size)
+{
+ void *vp = malloc(size);
+ if (!vp)
+ ccs_out_of_memory();
+ memset(vp, 0, size);
+ return vp;
+}
+
+/**
* ccs_str_starts - Check whether the given string starts with the given keyword.
*
* @str: Pointer to "char *".
}
/**
- * ccs_make_filename - Make filename using given prefix.
- *
- * @prefix: String to use as a prefix, including leading directories.
- * @time: A time_t value, usually return value of time(NULL).
- *
- * Returns pathname with timestamp embedded using static buffer.
- *
- * Note that this function is no longer used by anybody since 1.8.0p1.
- */
-char *ccs_make_filename(const char *prefix, const time_t time)
-{
- struct tm *tm = localtime(&time);
- static char filename[1024];
- memset(filename, 0, sizeof(filename));
- snprintf(filename, sizeof(filename) - 1,
- "%s.%02d-%02d-%02d.%02d:%02d:%02d.conf",
- prefix, tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
- return filename;
-}
-
-/**
* ccs_partial_name_hash - Hash name.
*
* @c: A unsigned long value.
}
/**
- * ccs_domain_def - Check whether the given token can be a domainname.
- *
- * @domainname: The token to check.
- *
- * Returns true if @domainname possibly be a domainname, false otherwise.
- *
- * Note that this function in kernel source checks only !strncmp() part.
- */
-_Bool ccs_domain_def(const char *domainname)
-{
- return !strncmp(domainname, CCS_ROOT_NAME, CCS_ROOT_NAME_LEN) &&
- (domainname[CCS_ROOT_NAME_LEN] == '\0'
- || domainname[CCS_ROOT_NAME_LEN] == ' ');
-}
-
-/**
* ccs_fprintf_encoded - fprintf() using TOMOYO's escape rules.
*
* @fp: Pointer to "FILE".
}
/**
+ * ccs_domain_def - Check whether the given token can be a domainname.
+ *
+ * @buffer: The token to check.
+ *
+ * Returns true if @buffer possibly be a domainname, false otherwise.
+ */
+_Bool ccs_domain_def(const char *buffer)
+{
+ const char *cp;
+ int len;
+ if (*buffer != '<')
+ return false;
+ cp = strchr(buffer, ' ');
+ if (!cp)
+ len = strlen(buffer);
+ else
+ len = cp - buffer;
+ return buffer[len - 1] == '>' &&
+ ccs_correct_word2(buffer + 1, len - 2);
+}
+
+/**
* ccs_correct_domain - Check whether the given domainname follows the naming rules.
*
* @domainname: The domainname to check.
*/
_Bool ccs_correct_domain(const char *domainname)
{
- if (!domainname || strncmp(domainname, CCS_ROOT_NAME,
- CCS_ROOT_NAME_LEN))
- goto out;
- domainname += CCS_ROOT_NAME_LEN;
- if (!*domainname)
+ if (!domainname || !ccs_domain_def(domainname))
+ return false;
+ domainname = strchr(domainname, ' ');
+ if (!domainname++)
return true;
- if (*domainname++ != ' ')
- goto out;
while (1) {
const char *cp = strchr(domainname, ' ');
if (!cp)
break;
if (*domainname != '/' ||
- !ccs_correct_word2(domainname, cp - domainname - 1))
+ !ccs_correct_word2(domainname, cp - domainname))
goto out;
domainname = cp + 1;
}
*
* @name: Pointer to "const char".
*
- * Returns pointer to "const struct ccs_path_info" on success, NULL otherwise.
+ * Returns pointer to "const struct ccs_path_info" on success, abort otherwise.
*
* The returned pointer refers shared string. Thus, the caller must not free().
*/
int len;
static _Bool first_call = true;
if (!name)
- return NULL;
+ ccs_out_of_memory();
len = strlen(name) + 1;
hash = ccs_full_name_hash((const unsigned char *) name, len - 1);
if (first_call) {
ccs_fill_path_info(&name_list[i].entry);
}
}
- ptr = &name_list[hash % CCS_SAVENAME_MAX_HASH];
- while (ptr) {
+ for (ptr = &name_list[hash % CCS_SAVENAME_MAX_HASH]; ptr;
+ ptr = ptr->next) {
if (hash == ptr->entry.hash && !strcmp(name, ptr->entry.name))
- goto out;
+ return &ptr->entry;
prev = ptr;
- ptr = ptr->next;
}
- ptr = malloc(sizeof(*ptr) + len);
- if (!ptr)
- ccs_out_of_memory();
+ ptr = ccs_malloc(sizeof(*ptr) + len);
ptr->next = NULL;
ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
memmove((void *) ptr->entry.name, name, len);
ccs_fill_path_info(&ptr->entry);
prev->next = ptr; /* prev != NULL because name_list is not empty. */
-out:
- return ptr ? &ptr->entry : NULL;
+ return &ptr->entry;
}
/**
return 0;
}
+/*
+ * Routines for parsing IPv4 or IPv6 address.
+ * These are copied from lib/hexdump.c net/core/utils.c .
+ */
+#include <ctype.h>
+
+static int hex_to_bin(char ch)
+{
+ if ((ch >= '0') && (ch <= '9'))
+ return ch - '0';
+ ch = tolower(ch);
+ if ((ch >= 'a') && (ch <= 'f'))
+ return ch - 'a' + 10;
+ return -1;
+}
+
+#define IN6PTON_XDIGIT 0x00010000
+#define IN6PTON_DIGIT 0x00020000
+#define IN6PTON_COLON_MASK 0x00700000
+#define IN6PTON_COLON_1 0x00100000 /* single : requested */
+#define IN6PTON_COLON_2 0x00200000 /* second : requested */
+#define IN6PTON_COLON_1_2 0x00400000 /* :: requested */
+#define IN6PTON_DOT 0x00800000 /* . */
+#define IN6PTON_DELIM 0x10000000
+#define IN6PTON_NULL 0x20000000 /* first/tail */
+#define IN6PTON_UNKNOWN 0x40000000
+
+static inline int xdigit2bin(char c, int delim)
+{
+ int val;
+
+ if (c == delim || c == '\0')
+ return IN6PTON_DELIM;
+ if (c == ':')
+ return IN6PTON_COLON_MASK;
+ if (c == '.')
+ return IN6PTON_DOT;
+
+ val = hex_to_bin(c);
+ if (val >= 0)
+ return val | IN6PTON_XDIGIT | (val < 10 ? IN6PTON_DIGIT : 0);
+
+ if (delim == -1)
+ return IN6PTON_DELIM;
+ return IN6PTON_UNKNOWN;
+}
+
+static int in4_pton(const char *src, int srclen, u8 *dst, int delim,
+ const char **end)
+{
+ const char *s;
+ u8 *d;
+ u8 dbuf[4];
+ int ret = 0;
+ int i;
+ int w = 0;
+
+ if (srclen < 0)
+ srclen = strlen(src);
+ s = src;
+ d = dbuf;
+ i = 0;
+ while(1) {
+ int c;
+ c = xdigit2bin(srclen > 0 ? *s : '\0', delim);
+ if (!(c & (IN6PTON_DIGIT | IN6PTON_DOT | IN6PTON_DELIM |
+ IN6PTON_COLON_MASK))) {
+ goto out;
+ }
+ if (c & (IN6PTON_DOT | IN6PTON_DELIM | IN6PTON_COLON_MASK)) {
+ if (w == 0)
+ goto out;
+ *d++ = w & 0xff;
+ w = 0;
+ i++;
+ if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) {
+ if (i != 4)
+ goto out;
+ break;
+ }
+ goto cont;
+ }
+ w = (w * 10) + c;
+ if ((w & 0xffff) > 255) {
+ goto out;
+ }
+cont:
+ if (i >= 4)
+ goto out;
+ s++;
+ srclen--;
+ }
+ ret = 1;
+ memcpy(dst, dbuf, sizeof(dbuf));
+out:
+ if (end)
+ *end = s;
+ return ret;
+}
+
+static int in6_pton(const char *src, int srclen, u8 *dst, int delim,
+ const char **end)
+{
+ const char *s, *tok = NULL;
+ u8 *d, *dc = NULL;
+ u8 dbuf[16];
+ int ret = 0;
+ int i;
+ int state = IN6PTON_COLON_1_2 | IN6PTON_XDIGIT | IN6PTON_NULL;
+ int w = 0;
+
+ memset(dbuf, 0, sizeof(dbuf));
+
+ s = src;
+ d = dbuf;
+ if (srclen < 0)
+ srclen = strlen(src);
+
+ while (1) {
+ int c;
+
+ c = xdigit2bin(srclen > 0 ? *s : '\0', delim);
+ if (!(c & state))
+ goto out;
+ if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) {
+ /* process one 16-bit word */
+ if (!(state & IN6PTON_NULL)) {
+ *d++ = (w >> 8) & 0xff;
+ *d++ = w & 0xff;
+ }
+ w = 0;
+ if (c & IN6PTON_DELIM) {
+ /* We've processed last word */
+ break;
+ }
+ /*
+ * COLON_1 => XDIGIT
+ * COLON_2 => XDIGIT|DELIM
+ * COLON_1_2 => COLON_2
+ */
+ switch (state & IN6PTON_COLON_MASK) {
+ case IN6PTON_COLON_2:
+ dc = d;
+ state = IN6PTON_XDIGIT | IN6PTON_DELIM;
+ if (dc - dbuf >= sizeof(dbuf))
+ state |= IN6PTON_NULL;
+ break;
+ case IN6PTON_COLON_1|IN6PTON_COLON_1_2:
+ state = IN6PTON_XDIGIT | IN6PTON_COLON_2;
+ break;
+ case IN6PTON_COLON_1:
+ state = IN6PTON_XDIGIT;
+ break;
+ case IN6PTON_COLON_1_2:
+ state = IN6PTON_COLON_2;
+ break;
+ default:
+ state = 0;
+ }
+ tok = s + 1;
+ goto cont;
+ }
+
+ if (c & IN6PTON_DOT) {
+ ret = in4_pton(tok ? tok : s, srclen + (int)(s - tok),
+ d, delim, &s);
+ if (ret > 0) {
+ d += 4;
+ break;
+ }
+ goto out;
+ }
+
+ w = (w << 4) | (0xff & c);
+ state = IN6PTON_COLON_1 | IN6PTON_DELIM;
+ if (!(w & 0xf000)) {
+ state |= IN6PTON_XDIGIT;
+ }
+ if (!dc && d + 2 < dbuf + sizeof(dbuf)) {
+ state |= IN6PTON_COLON_1_2;
+ state &= ~IN6PTON_DELIM;
+ }
+ if (d + 2 >= dbuf + sizeof(dbuf)) {
+ state &= ~(IN6PTON_COLON_1|IN6PTON_COLON_1_2);
+ }
+cont:
+ if ((dc && d + 4 < dbuf + sizeof(dbuf)) ||
+ d + 4 == dbuf + sizeof(dbuf)) {
+ state |= IN6PTON_DOT;
+ }
+ if (d >= dbuf + sizeof(dbuf)) {
+ state &= ~(IN6PTON_XDIGIT|IN6PTON_COLON_MASK);
+ }
+ s++;
+ srclen--;
+ }
+
+ i = 15; d--;
+
+ if (dc) {
+ while(d >= dc)
+ dst[i--] = *d--;
+ while(i >= dc - dbuf)
+ dst[i--] = 0;
+ while(i >= 0)
+ dst[i--] = *d--;
+ } else
+ memcpy(dst, dbuf, sizeof(dbuf));
+
+ ret = 1;
+out:
+ if (end)
+ *end = s;
+ return ret;
+}
+
/**
* ccs_parse_ip - Parse a ccs_ip_address_entry.
*
*/
int ccs_parse_ip(const char *address, struct ccs_ip_address_entry *entry)
{
- unsigned int min[8];
- unsigned int max[8];
- int i;
- int j;
- memset(entry, 0, sizeof(*entry));
- i = sscanf(address, "%u.%u.%u.%u-%u.%u.%u.%u",
- &min[0], &min[1], &min[2], &min[3],
- &max[0], &max[1], &max[2], &max[3]);
- if (i == 4)
- for (j = 0; j < 4; j++)
- max[j] = min[j];
- if (i == 4 || i == 8) {
- for (j = 0; j < 4; j++) {
- entry->min[j] = (u8) min[j];
- entry->max[j] = (u8) max[j];
- }
+ u8 * const min = entry->min;
+ u8 * const max = entry->max;
+ const char *end;
+ memset(entry, 0, sizeof(entry));
+ if (!strchr(address, ':') &&
+ in4_pton(address, -1, min, '-', &end) > 0) {
+ entry->is_ipv6 = false;
+ if (!*end)
+ memmove(max, min, 4);
+ else if (*end++ != '-' ||
+ in4_pton(end, -1, max, '\0', &end) <= 0 || *end)
+ return -EINVAL;
return 0;
}
- i = sscanf(address, "%X:%X:%X:%X:%X:%X:%X:%X-%X:%X:%X:%X:%X:%X:%X:%X",
- &min[0], &min[1], &min[2], &min[3],
- &min[4], &min[5], &min[6], &min[7],
- &max[0], &max[1], &max[2], &max[3],
- &max[4], &max[5], &max[6], &max[7]);
- if (i == 8)
- for (j = 0; j < 8; j++)
- max[j] = min[j];
- if (i == 8 || i == 16) {
- for (j = 0; j < 8; j++) {
- entry->min[j * 2] = (u8) (min[j] >> 8);
- entry->min[j * 2 + 1] = (u8) min[j];
- entry->max[j * 2] = (u8) (max[j] >> 8);
- entry->max[j * 2 + 1] = (u8) max[j];
- }
+ if (in6_pton(address, -1, min, '-', &end) > 0) {
entry->is_ipv6 = true;
+ if (!*end)
+ memmove(max, min, 16);
+ else if (*end++ != '-' ||
+ in6_pton(end, -1, max, '\0', &end) <= 0 || *end)
+ return -EINVAL;
return 0;
}
return -EINVAL;
* @is_dd: True if the domain is marked as deleted, false otherwise.
*
* Returns index number (>= 0) in the @dp array if created or already exists,
- * EOF otherwise.
+ * abort otherwise.
*/
int ccs_assign_domain(struct ccs_domain_policy *dp, const char *domainname,
const _Bool is_dis, const _Bool is_dd)
{
- const struct ccs_path_info *saved_domainname;
int index = ccs_find_domain(dp, domainname, is_dis, is_dd);
if (index >= 0)
- goto found;
+ return index;
if (!is_dis && !ccs_correct_domain(domainname)) {
- fprintf(stderr, "Invalid domainname '%s'\n",
- domainname);
- return EOF;
- }
- dp->list = realloc(dp->list, (dp->list_len + 1) *
- sizeof(struct ccs_domain_info));
- if (!dp->list)
- ccs_out_of_memory();
- memset(&dp->list[dp->list_len], 0,
- sizeof(struct ccs_domain_info));
- saved_domainname = ccs_savename(domainname);
- if (!saved_domainname)
+ fprintf(stderr, "Invalid domainname '%s'\n", domainname);
ccs_out_of_memory();
- dp->list[dp->list_len].domainname = saved_domainname;
- dp->list[dp->list_len].is_dis = is_dis;
- dp->list[dp->list_len].is_dd = is_dd;
+ }
index = dp->list_len++;
-found:
+ dp->list = ccs_realloc(dp->list, dp->list_len *
+ sizeof(struct ccs_domain_info));
+ memset(&dp->list[index], 0, sizeof(struct ccs_domain_info));
+ dp->list[index].domainname = ccs_savename(domainname);
+ dp->list[index].is_dis = is_dis;
+ dp->list[index].is_dd = is_dd;
return index;
}
}
/**
+ * ccs_add_process_entry - Add entry for running processes.
+ *
+ * @line: A line containing PID and profile and domainname.
+ * @ppid: Parent PID.
+ * @name: Comm name (allocated by strdup()).
+ *
+ * Returns nothing.
+ *
+ * @name is free()d on failure.
+ */
+static void ccs_add_process_entry(const char *line, const pid_t ppid,
+ char *name)
+{
+ int index;
+ unsigned int pid = 0;
+ int profile = -1;
+ char *domain;
+ if (!line || sscanf(line, "%u %u", &pid, &profile) != 2) {
+ free(name);
+ return;
+ }
+ domain = strchr(line, '<');
+ if (domain)
+ domain = ccs_strdup(domain);
+ else
+ domain = ccs_strdup("<UNKNOWN>");
+ index = ccs_task_list_len++;
+ ccs_task_list = ccs_realloc(ccs_task_list, ccs_task_list_len *
+ sizeof(struct ccs_task_entry));
+ memset(&ccs_task_list[index], 0, sizeof(ccs_task_list[0]));
+ ccs_task_list[index].pid = pid;
+ ccs_task_list[index].ppid = ppid;
+ ccs_task_list[index].profile = profile;
+ ccs_task_list[index].name = name;
+ ccs_task_list[index].domain = domain;
+}
+
+/**
* ccs_read_process_list - Read all process's information.
*
* @show_all: Ture if kernel threads should be included, false otherwise.
char *line = ccs_freadline(fp);
unsigned int pid = 0;
unsigned int ppid = 0;
- int profile = -1;
char *name;
- char *domain;
if (!line)
break;
sscanf(line, "PID=%u PPID=%u", &pid, &ppid);
name = strstr(line, "NAME=");
if (name)
- name = strdup(name + 5);
- if (!name)
- name = strdup("<UNKNOWN>");
- if (!name)
- ccs_out_of_memory();
+ name = ccs_strdup(name + 5);
+ else
+ name = ccs_strdup("<UNKNOWN>");
line = ccs_freadline(fp);
- if (!line ||
- sscanf(line, "%u %u", &pid, &profile) != 2) {
- free(name);
- break;
- }
- domain = strchr(line, '<');
- if (domain)
- domain = strdup(domain);
- if (!domain)
- domain = strdup("<UNKNOWN>");
- if (!domain)
- ccs_out_of_memory();
- ccs_task_list = realloc(ccs_task_list,
- (ccs_task_list_len + 1) *
- sizeof(struct ccs_task_entry));
- if (!ccs_task_list)
- ccs_out_of_memory();
- memset(&ccs_task_list[ccs_task_list_len], 0,
- sizeof(ccs_task_list[0]));
- ccs_task_list[ccs_task_list_len].pid = pid;
- ccs_task_list[ccs_task_list_len].ppid = ppid;
- ccs_task_list[ccs_task_list_len].profile = profile;
- ccs_task_list[ccs_task_list_len].name = name;
- ccs_task_list[ccs_task_list_len].domain = domain;
- ccs_task_list_len++;
+ ccs_add_process_entry(line, ppid, name);
}
ccs_put();
fclose(fp);
closedir(dir);
return;
}
- line = malloc(line_len);
- if (!line)
- ccs_out_of_memory();
+ line = ccs_malloc(line_len);
while (1) {
char *name;
- char *domain;
- int profile = -1;
int ret_ignored;
unsigned int pid = 0;
char buffer[128];
}
name = ccs_get_name(pid);
if (!name)
- name = strdup("<UNKNOWN>");
- if (!name)
- ccs_out_of_memory();
+ name = ccs_strdup("<UNKNOWN>");
snprintf(buffer, sizeof(buffer) - 1, "%u\n", pid);
ret_ignored = write(status_fd, buffer, strlen(buffer));
memset(line, 0, line_len);
ret_ignored = read(status_fd, line, line_len - 1);
- if (sscanf(line, "%u %u", &pid, &profile) != 2) {
- free(name);
- continue;
- }
- domain = strchr(line, '<');
- if (domain)
- domain = strdup(domain);
- if (!domain)
- domain = strdup("<UNKNOWN>");
- if (!domain)
- ccs_out_of_memory();
- ccs_task_list = realloc(ccs_task_list,
- (ccs_task_list_len + 1) *
- sizeof(struct ccs_task_entry));
- if (!ccs_task_list)
- ccs_out_of_memory();
- memset(&ccs_task_list[ccs_task_list_len], 0,
- sizeof(ccs_task_list[0]));
- ccs_task_list[ccs_task_list_len].pid = pid;
- ccs_task_list[ccs_task_list_len].ppid =
- ccs_get_ppid(pid);
- ccs_task_list[ccs_task_list_len].profile = profile;
- ccs_task_list[ccs_task_list_len].name = name;
- ccs_task_list[ccs_task_list_len].domain = domain;
- ccs_task_list_len++;
+ ccs_add_process_entry(line, ccs_get_ppid(pid), name);
}
free(line);
closedir(dir);
}
/**
- * ccs_identical_file - Check whether two files are identical or not.
- *
- * @file1: Pointer to "const char ".
- * @file2: Pointer to "const char".
- *
- * Returns true if both @file1 and @file2 exist and are readable and are
- * identical, false otherwise.
- *
- * Note that this function is no longer used by anybody since 1.8.0p1.
- */
-_Bool ccs_identical_file(const char *file1, const char *file2)
-{
- char buffer1[4096];
- char buffer2[4096];
- struct stat sb1;
- struct stat sb2;
- const int fd1 = open(file1, O_RDONLY);
- const int fd2 = open(file2, O_RDONLY);
- int len1;
- int len2;
- /* Don't compare if file1 is a symlink to file2. */
- if (fstat(fd1, &sb1) || fstat(fd2, &sb2) || sb1.st_ino == sb2.st_ino)
- goto out;
- do {
- len1 = read(fd1, buffer1, sizeof(buffer1));
- len2 = read(fd2, buffer2, sizeof(buffer2));
- if (len1 < 0 || len1 != len2)
- goto out;
- if (memcmp(buffer1, buffer2, len1))
- goto out;
- } while (len1);
- close(fd1);
- close(fd2);
- return true;
-out:
- close(fd1);
- close(fd2);
- return false;
-}
-
-/**
* ccs_clear_domain_policy - Clean up domain policy.
*
* @dp: Pointer to "struct ccs_domain_policy".
if (!entry || !*entry)
return -EINVAL;
cp = ccs_savename(entry);
- if (!cp)
- ccs_out_of_memory();
acl_ptr = dp->list[index].string_ptr;
acl_count = dp->list[index].string_count;
/* Check for the same entry. */
- for (i = 0; i < acl_count; i++) {
+ for (i = 0; i < acl_count; i++)
/* Faster comparison, for they are ccs_savename'd. */
if (cp == acl_ptr[i])
return 0;
- }
- acl_ptr = realloc(acl_ptr, (acl_count + 1)
- * sizeof(const struct ccs_path_info *));
- if (!acl_ptr)
- ccs_out_of_memory();
+ acl_ptr = ccs_realloc(acl_ptr, (acl_count + 1) *
+ sizeof(const struct ccs_path_info *));
acl_ptr[acl_count++] = cp;
dp->list[index].string_ptr = acl_ptr;
dp->list[index].string_count = acl_count;
if (!entry || !*entry)
return -EINVAL;
cp = ccs_savename(entry);
- if (!cp)
- ccs_out_of_memory();
acl_ptr = dp->list[index].string_ptr;
acl_count = dp->list[index].string_count;
if (len < 0)
ccs_out_of_memory();
if (len >= max_policy_len) {
- char *cp;
max_policy_len = len + 1;
- cp = realloc(policy, max_policy_len);
- if (!cp)
- ccs_out_of_memory();
- policy = cp;
+ policy = ccs_realloc(policy, max_policy_len);
} else
return policy;
}
if (ccs_network_mode && !c)
return NULL;
if (pos == max_policy_len) {
- char *cp;
max_policy_len += 4096;
- cp = realloc(policy, max_policy_len);
- if (!cp)
- ccs_out_of_memory();
- policy = cp;
+ policy = ccs_realloc(policy, max_policy_len);
}
policy[pos++] = (char) c;
if (c == '\n') {
char *line = ccs_freadline(fp);
if (!line)
return NULL;
- if (sscanf(line, "acl_group %u", &offset) == 1 && offset < 256)
- pos = strchr(line + 11, ' ');
+ /*
+ * Skip
+ * <$namespace>
+ * prefix unless this line represents a domainname.
+ */
+ if (ccs_domain_def(line) && !ccs_correct_domain(line)) {
+ pos = strchr(line, ' ');
+ if (!pos++)
+ pos = line;
+ } else
+ pos = line;
+ /*
+ * Skip
+ * acl_group $group
+ * prefix if this line is a line of exception policy.
+ */
+ if (sscanf(pos, "acl_group %u", &offset) == 1 && offset < 256)
+ pos = strchr(pos + 11, ' ');
else
pos = NULL;
if (pos++)
offset = pos - line;
else
offset = 0;
+ /*
+ * Only "file " and "network " are subjected to unpacking.
+ */
if (!strncmp(line + offset, "file ", 5)) {
char *cp = line + offset + 5;
char *cp2 = strchr(cp + 1, ' ');
return line;
prepare:
pack_len = len;
- cached_line = strdup(line);
- if (!cached_line)
- ccs_out_of_memory();
+ cached_line = ccs_strdup(line);
}
unpack:
{
cached_line = NULL;
} else {
/* Current string is "abc d/e/f ghi". */
- line = strdup(cached_line);
- if (!line)
- ccs_out_of_memory();
+ line = ccs_strdup(cached_line);
previous_line = line;
/* Overwrite "abc d/e/f ghi" with "abc d ghi". */
memmove(line + pack_start + len, pos + pack_len,
*
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*
- * Version: 1.8.1 2011/04/01
+ * Version: 1.8.2+ 2011/07/07
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License v2 as published by the
/***** CONSTANTS DEFINITION START *****/
-#define CCS_ROOT_NAME "<kernel>"
-#define CCS_ROOT_NAME_LEN (sizeof(CCS_ROOT_NAME) - 1)
-
#define CCS_PROC_POLICY_DIR "/proc/ccs/"
#define CCS_PROC_POLICY_DOMAIN_POLICY "/proc/ccs/domain_policy"
-#define CCS_PROC_POLICY_DOMAIN_STATUS "/proc/ccs/.domain_status"
#define CCS_PROC_POLICY_EXCEPTION_POLICY "/proc/ccs/exception_policy"
#define CCS_PROC_POLICY_AUDIT "/proc/ccs/audit"
#define CCS_PROC_POLICY_MANAGER "/proc/ccs/manager"
_Bool ccs_correct_word(const char *string);
_Bool ccs_decode(const char *ascii, char *bin);
_Bool ccs_domain_def(const char *domainname);
-_Bool ccs_identical_file(const char *file1, const char *file2);
_Bool ccs_move_proc_to_file(const char *src, const char *dest);
_Bool ccs_path_matches_pattern(const struct ccs_path_info *pathname0,
const struct ccs_path_info *pattern0);
-_Bool ccs_pathcmp(const struct ccs_path_info *a, const struct ccs_path_info *b);
+_Bool ccs_pathcmp(const struct ccs_path_info *a,
+ const struct ccs_path_info *b);
_Bool ccs_str_starts(char *str, const char *begin);
char *ccs_freadline(FILE *fp);
char *ccs_freadline_unpack(FILE *fp);
-char *ccs_make_filename(const char *prefix, const time_t time);
-char *ccs_shprintf(const char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
+char *ccs_shprintf(const char *fmt, ...)
+ __attribute__ ((format(printf, 1, 2)));
+char *ccs_strdup(const char *string);
const char *ccs_domain_name(const struct ccs_domain_policy *dp,
const int index);
const struct ccs_path_info *ccs_savename(const char *name);
const _Bool is_dis, const _Bool is_dd);
int ccs_del_string_entry(struct ccs_domain_policy *dp, const char *entry,
const int index);
-int ccs_find_domain(const struct ccs_domain_policy *dp, const char *domainname0,
- const _Bool is_dis, const _Bool is_dd);
+int ccs_find_domain(const struct ccs_domain_policy *dp,
+ const char *domainname0, const _Bool is_dis,
+ const _Bool is_dd);
int ccs_find_domain_by_ptr(struct ccs_domain_policy *dp,
const struct ccs_path_info *domainname);
int ccs_open_stream(const char *filename);
int ccs_string_compare(const void *a, const void *b);
int ccs_write_domain_policy(struct ccs_domain_policy *dp, const int fd);
struct ccs_path_group_entry *ccs_find_path_group(const char *group_name);
+void *ccs_malloc(const size_t size);
+void *ccs_realloc(void *ptr, const size_t size);
+void *ccs_realloc2(void *ptr, const size_t size);
void ccs_clear_domain_policy(struct ccs_domain_policy *dp);
void ccs_delete_domain(struct ccs_domain_policy *dp, const int index);
void ccs_fill_path_info(struct ccs_path_info *ptr);
void ccs_handle_domain_policy(struct ccs_domain_policy *dp, FILE *fp,
_Bool is_write);
void ccs_normalize_line(char *buffer);
-void ccs_out_of_memory(void);
void ccs_put(void);
-void ccs_read_domain_policy(struct ccs_domain_policy *dp, const char *filename);
+void ccs_read_domain_policy(struct ccs_domain_policy *dp,
+ const char *filename);
void ccs_read_process_list(_Bool show_all);
extern _Bool ccs_freadline_raw;
*
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*
- * Version: 1.8.1 2011/04/01
+ * Version: 1.8.2+ 2011/07/11
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License v2 as published by the
#endif /* __GPET */
/* Domain policy. */
-struct ccs_domain_policy ccs_dp = { };
+struct ccs_domain_policy3 ccs_dp = { };
/* Readline history. */
static struct ccs_readline_data ccs_rl = { };
-/* File descriptor for offline mode. */
-int ccs_persistent_fd = EOF;
-
/* Array of "path_group" entries. */
struct ccs_path_group_entry *ccs_path_group_list = NULL;
/* Length of ccs_path_group_list array. */
/* Array of string ACL entries. */
struct ccs_generic_acl *ccs_gacl_list = NULL;
/* Length of ccs_generic_list array. */
-int ccs_gacl_list_count = 0;
+static int ccs_gacl_list_count = 0;
/* Policy directory. */
static const char *ccs_policy_dir = NULL;
static unsigned int ccs_current_pid = 0;
/* Currently active screen's index. */
enum ccs_screen_type ccs_current_screen = CCS_SCREEN_DOMAIN_LIST;
+/* Previously active screen's index. */
+static enum ccs_screen_type ccs_previous_screen = CCS_SCREEN_DOMAIN_LIST;
/*
* Array of "initialize_domain"/"no_initialize_domain"/"keep_domain"/
* "no_keep_domain" entries.
static int ccs_transition_control_list_len = 0;
/* Sort profiles by value? */
static _Bool ccs_profile_sort_type = false;
-/* Number of domain initializer source domains. */
+/* Number of domain jump source domains. */
static int ccs_unnumbered_domain_count = 0;
/* Width of CUI screen. */
static int ccs_window_width = 0;
struct ccs_screen ccs_screen[CCS_MAXSCREEN] = { };
/* Number of entries available on current screen. */
int ccs_list_item_count = 0;
-/* Lines available for displaying ACL entries. */
+/* Lines available for displaying ACL entries. */
static int ccs_body_lines = 0;
/* Columns to shift. */
static int ccs_eat_col = 0;
static _Bool ccs_domain_sort_type = false;
/* Start from the first line when showing ACL screen? */
static _Bool ccs_no_restore_cursor = false;
+static _Bool ccs_force_move_cursor = false;
+
+/* Namespace to use. */
+const struct ccs_path_info *ccs_current_ns = NULL;
/* Domain transition coltrol keywords. */
static const char *ccs_transition_type[CCS_MAX_TRANSITION_TYPE] = {
- [CCS_TRANSITION_CONTROL_INITIALIZE] = "initialize_domain ",
+ [CCS_TRANSITION_CONTROL_RESET] = "reset_domain ",
+ [CCS_TRANSITION_CONTROL_NO_RESET] = "no_reset_domain ",
+ [CCS_TRANSITION_CONTROL_INITIALIZE] = "initialize_domain ",
[CCS_TRANSITION_CONTROL_NO_INITIALIZE] = "no_initialize_domain ",
- [CCS_TRANSITION_CONTROL_KEEP] = "keep_domain ",
- [CCS_TRANSITION_CONTROL_NO_KEEP] = "no_keep_domain ",
+ [CCS_TRANSITION_CONTROL_KEEP] = "keep_domain ",
+ [CCS_TRANSITION_CONTROL_NO_KEEP] = "no_keep_domain ",
};
static FILE *ccs_editpolicy_open_write(const char *filename);
static _Bool ccs_deleted_domain(const int index);
static _Bool ccs_domain_unreachable(const int index);
-static _Bool ccs_initializer_source(const int index);
-static _Bool ccs_initializer_target(const int index);
+static _Bool ccs_jump_source(const int index);
+static _Bool ccs_jump_target(const int index);
static _Bool ccs_keeper_domain(const int index);
static _Bool ccs_select_item(const int index);
static _Bool ccs_show_command_key(const enum ccs_screen_type screen,
static const char *ccs_eat(const char *str);
static const char *ccs_get_last_name(const int index);
static const struct ccs_transition_control_entry *ccs_transition_control
-(const struct ccs_path_info *domainname, const char *program);
+(const struct ccs_path_info *ns, const char *domainname, const char *program);
static enum ccs_screen_type ccs_generic_list_loop(void);
static enum ccs_screen_type ccs_select_window(const int current);
-static int ccs_add_path_group_entry(const char *group_name,
+static int ccs_add_path_group_entry(const struct ccs_path_info *ns,
+ const char *group_name,
const char *member_name,
const _Bool is_delete);
-static int ccs_add_path_group_policy(char *data, const _Bool is_delete);
-static int ccs_add_transition_control_entry(const char *domainname,
+static int ccs_add_path_group_policy(const struct ccs_path_info *ns,
+ char *data, const _Bool is_delete);
+static int ccs_add_transition_control_entry(const struct ccs_path_info *ns,
+ const char *domainname,
const char *program, const enum
ccs_transition_type type);
-static int ccs_add_transition_control_policy(char *data, const enum
+static int ccs_add_transition_control_policy(const struct ccs_path_info *ns,
+ char *data, const enum
ccs_transition_type type);
static int ccs_count(const unsigned char *array, const int len);
static int ccs_count2(const struct ccs_generic_acl *array, int len);
static int ccs_string_acl_compare(const void *a, const void *b);
static void ccs_add_entry(void);
static void ccs_adjust_cursor_pos(const int item_count);
-static void ccs_assign_dis(const struct ccs_path_info *domainname,
- const char *program);
+static void ccs_assign_djs(const struct ccs_path_info *ns,
+ const char *domainname, const char *program);
static void ccs_copy_file(const char *source, const char *dest);
static void ccs_delete_entry(const int index);
static void ccs_down_arrow_key(void);
static void ccs_up_arrow_key(void);
/**
+ * ccs_find_domain3 - Find a domain by name and other attributes.
+ *
+ * @domainname: Name of domain to find.
+ * @target: Name of target to find. Maybe NULL.
+ * @is_dd: True if the domain is marked as deleted, false otherwise.
+ *
+ * Returns index number (>= 0) if found, EOF otherwise.
+ */
+static int ccs_find_domain3(const char *domainname, const char *target,
+ const _Bool is_dd)
+{
+ int i;
+ for (i = 0; i < ccs_dp.list_len; i++) {
+ const struct ccs_domain *ptr = &ccs_dp.list[i];
+ if (ptr->is_dd == is_dd &&
+ ((!ptr->target && !target) ||
+ (ptr->target && target &&
+ !strcmp(ptr->target->name, target))) &&
+ !strcmp(ptr->domainname->name, domainname))
+ return i;
+ }
+ return EOF;
+}
+
+/**
+ * ccs_find_domain3_by_name - Find a domain by name.
+ *
+ * @domainname: Name of domain to find.
+ *
+ * Returns pointer to "struct ccs_domain" if found, NULL otherwise.
+ */
+static struct ccs_domain *ccs_find_domain3_by_name(const char *domainname)
+{
+ int i;
+ for (i = 0; i < ccs_dp.list_len; i++) {
+ struct ccs_domain *ptr = &ccs_dp.list[i];
+ if (!ptr->target && !strcmp(ptr->domainname->name, domainname))
+ return ptr;
+ }
+ return NULL;
+}
+
+/**
+ * ccs_assign_domain3 - Create a domain by name and other attributes.
+ *
+ * @domainname: Name of domain to find.
+ * @target: Name of target domain if the domain acts as domain jump source,
+ * NULL otherwise.
+ * @is_dd: True if the domain is marked as deleted, false otherwise.
+ *
+ * Returns index number (>= 0) if created or already exists, abort otherwise.
+ */
+static int ccs_assign_domain3(const char *domainname, const char *target,
+ const _Bool is_dd)
+{
+ struct ccs_domain *ptr;
+ int index = ccs_find_domain3(domainname, target, is_dd);
+ if (index >= 0)
+ return index;
+ index = ccs_dp.list_len++;
+ ccs_dp.list = ccs_realloc(ccs_dp.list, ccs_dp.list_len *
+ sizeof(struct ccs_domain));
+ ptr = &ccs_dp.list[index];
+ memset(ptr, 0, sizeof(*ptr));
+ ptr->domainname = ccs_savename(domainname);
+ if (target)
+ ptr->target = ccs_savename(target);
+ ptr->is_dd = is_dd;
+ return index;
+}
+
+/**
+ * ccs_add_string_entry - Add string entry to a domain.
+ *
+ * @entry: String to add.
+ * @index: Index in the @dp array.
+ *
+ * Returns 0 if successfully added or already exists, -EINVAL otherwise.
+ */
+static int ccs_add_string_entry3(const char *entry, const int index)
+{
+ const struct ccs_path_info **acl_ptr;
+ int acl_count;
+ const struct ccs_path_info *cp;
+ int i;
+ if (index < 0 || index >= ccs_dp.list_len) {
+ fprintf(stderr, "ERROR: domain is out of range.\n");
+ return -EINVAL;
+ }
+ if (!entry || !*entry)
+ return -EINVAL;
+ cp = ccs_savename(entry);
+
+ acl_ptr = ccs_dp.list[index].string_ptr;
+ acl_count = ccs_dp.list[index].string_count;
+
+ /* Check for the same entry. */
+ for (i = 0; i < acl_count; i++)
+ /* Faster comparison, for they are ccs_savename'd. */
+ if (cp == acl_ptr[i])
+ return 0;
+
+ acl_ptr = ccs_realloc(acl_ptr, (acl_count + 1) *
+ sizeof(const struct ccs_path_info *));
+ acl_ptr[acl_count++] = cp;
+ ccs_dp.list[index].string_ptr = acl_ptr;
+ ccs_dp.list[index].string_count = acl_count;
+ return 0;
+}
+
+/**
+ * ccs_clear_domain_policy3 - Clean up domain policy.
+ *
+ * Returns nothing.
+ */
+static void ccs_clear_domain_policy3(void)
+{
+ int index;
+ for (index = 0; index < ccs_dp.list_len; index++) {
+ free(ccs_dp.list[index].string_ptr);
+ ccs_dp.list[index].string_ptr = NULL;
+ ccs_dp.list[index].string_count = 0;
+ }
+ free(ccs_dp.list);
+ ccs_dp.list = NULL;
+ ccs_dp.list_len = 0;
+}
+
+/**
+ * ccs_is_same_namespace - Check namespace.
+ *
+ * @domain: Domainname.
+ * @ns: Namespace.
+ *
+ * Returns true if same namespace, false otherwise.
+ */
+static _Bool ccs_is_same_namespace(const char *domain,
+ const struct ccs_path_info *ns)
+{
+ return !strncmp(domain, ns->name, ns->total_len) &&
+ (domain[ns->total_len] == ' ' || !domain[ns->total_len]);
+}
+
+/**
+ * ccs_is_current_namespace - Check namespace.
+ *
+ * @line: Line to check namespace.
+ *
+ * Returns true if this line deals current namespace, false otherwise.
+ */
+static _Bool ccs_is_current_namespace(const char *line)
+{
+ return ccs_is_same_namespace(line, ccs_current_ns);
+}
+
+/**
* ccs_copy_file - Copy local file to local or remote file.
*
* @source: Local file.
}
/**
+ * ccs_get_ns - Get namespace component from domainname.
+ *
+ * @domainname: A domainname.
+ *
+ * Returns the namespace component of @domainname.
+ */
+static const struct ccs_path_info *ccs_get_ns(const char *domainname)
+{
+ const struct ccs_path_info *ns;
+ char *line = ccs_strdup(domainname);
+ char *cp;
+ cp = strchr(line, ' ');
+ if (cp)
+ *cp = '\0';
+ ns = ccs_savename(line);
+ free(line);
+ return ns;
+}
+
+/**
+ * ccs_get_last_word - Get last component of a line.
+ *
+ * @line: A line of words.
+ *
+ * Returns the last component of the line.
+ */
+static const char *ccs_get_last_word(const char *line)
+{
+ const char *cp = strrchr(line, ' ');
+ if (cp)
+ return cp + 1;
+ return line;
+}
+
+/**
* ccs_get_last_name - Get last component of a domainname.
*
* @index: Index in the domain policy.
*
- * Returns the last componet of the domainname.
+ * Returns the last component of the domainname.
*/
static const char *ccs_get_last_name(const int index)
{
- const char *cp0 = ccs_domain_name(&ccs_dp, index);
- const char *cp1 = strrchr(cp0, ' ');
- if (cp1)
- return cp1 + 1;
- return cp0;
+ return ccs_get_last_word(ccs_dp.list[index].domainname->name);
}
/**
}
/**
- * ccs_keeper_domain - Check whether the given domain is marked as "keep_domain".
+ * ccs_keeper_domain - Check whether the given domain is marked as keeper or not.
*
* @index: Index in the domain policy.
*
}
/**
- * ccs_initializer_source - Check whether the given domain is marked as "initialize_domain".
+ * ccs_jump_source - Check whether the given domain is marked as jump source or not.
*
* @index: Index in the domain policy.
*
- * Returns true if the given domain is marked as "initialize_domain",
+ * Returns true if the given domain is marked as domain jump source,
* false otherwise.
*/
-static _Bool ccs_initializer_source(const int index)
+static _Bool ccs_jump_source(const int index)
{
- return ccs_dp.list[index].is_dis;
+ return ccs_dp.list[index].target != NULL;
}
/**
- * ccs_initializer_target - Check whether the given domain is a target of "initialize_domain".
+ * ccs_jump_target - Check whether the given domain is marked as jump target or not.
*
* @index: Index in the domain policy.
*
- * Returns true if the given domain is a target of "initialize_domain",
- * false otherwise.
+ * Returns true if the given domain is a domain jump target, false otherwise.
*/
-static _Bool ccs_initializer_target(const int index)
+static _Bool ccs_jump_target(const int index)
{
- return ccs_dp.list[index].is_dit;
+ return ccs_dp.list[index].is_djt;
}
/**
- * ccs_domain_unreachable - Check whether the given domain is unreachable or not.
+ * ccs_domain_unreachable - Check whether the given domain is marked as unreachable or not.
*
* @index: Index in the domain policy.
*
* Returns true if the given domain is unreachable, false otherwise.
- *
- * TODO: Check "task auto_domain_transition" and
- * "task manual_domain_transition" which allow other domains to reach
- * the given domain.
*/
static _Bool ccs_domain_unreachable(const int index)
{
}
/**
- * ccs_add_transition_control_policy - Add "initialize_domain"/"no_initialize_domain"/"keep_domain"/ "no_keep_domain" entries.
+ * ccs_add_transition_control_policy - Add "reset_domain"/"no_reset_domain"/"initialize_domain"/"no_initialize_domain"/"keep_domain"/"no_keep_domain" entries.
*
+ * @ns: Pointer to "const struct ccs_path_info".
* @data: Line to parse.
* @type: One of values in "enum ccs_transition_type".
*
* Returns 0 on success, negative value otherwise.
*/
static int ccs_add_transition_control_policy
-(char *data, const enum ccs_transition_type type)
+(const struct ccs_path_info *ns, char *data,
+ const enum ccs_transition_type type)
{
char *domainname = strstr(data, " from ");
if (domainname) {
domainname = data;
data = NULL;
}
- return ccs_add_transition_control_entry(domainname, data, type);
+ return ccs_add_transition_control_entry(ns, domainname, data, type);
}
/**
* ccs_add_path_group_policy - Add "path_group" entry.
*
+ * @ns: Pointer to "const struct ccs_path_info".
* @data: Line to parse.
* @is_delete: True if it is delete request, false otherwise.
*
* Returns 0 on success, negative value otherwise.
*/
-static int ccs_add_path_group_policy(char *data, const _Bool is_delete)
+static int ccs_add_path_group_policy(const struct ccs_path_info *ns,
+ char *data, const _Bool is_delete)
{
char *cp = strchr(data, ' ');
if (!cp)
return -EINVAL;
*cp++ = '\0';
- return ccs_add_path_group_entry(data, cp, is_delete);
+ return ccs_add_path_group_entry(ns, data, cp, is_delete);
}
/**
- * ccs_assign_dis - Assign domain initializer source domain.
+ * ccs_assign_djs - Assign domain jump source domain.
*
- * @domainname: Pointer to "const struct ccs_path_info".
+ * @ns: Pointer to "const struct ccs_path_info".
+ * @domainname: Domainname.
* @program: Program name.
*/
-static void ccs_assign_dis(const struct ccs_path_info *domainname,
- const char *program)
+static void ccs_assign_djs(const struct ccs_path_info *ns,
+ const char *domainname, const char *program)
{
const struct ccs_transition_control_entry *d_t =
- ccs_transition_control(domainname, program);
- if (d_t && d_t->type == CCS_TRANSITION_CONTROL_INITIALIZE) {
+ ccs_transition_control(ns, domainname, program);
+ if (!d_t)
+ return;
+ if (d_t->type == CCS_TRANSITION_CONTROL_INITIALIZE ||
+ d_t->type == CCS_TRANSITION_CONTROL_RESET) {
char *line;
- int source;
+ char *cp;
ccs_get();
- line = ccs_shprintf("%s %s", domainname->name, program);
+ if (d_t->type == CCS_TRANSITION_CONTROL_INITIALIZE)
+ line = ccs_shprintf("%s %s", domainname, program);
+ else
+ line = ccs_shprintf("%s <%s>", domainname, program);
ccs_normalize_line(line);
- source = ccs_assign_domain(&ccs_dp, line, true, false);
- if (source == EOF)
- ccs_out_of_memory();
- line = ccs_shprintf(CCS_ROOT_NAME " %s", program);
- ccs_dp.list[source].target_domainname = strdup(line);
- if (!ccs_dp.list[source].target_domainname)
- ccs_out_of_memory();
+ cp = ccs_strdup(line);
+ if (d_t->type == CCS_TRANSITION_CONTROL_INITIALIZE)
+ line = ccs_shprintf("%s %s", ns->name, program);
+ else
+ line = ccs_shprintf("<%s>", program);
+ ccs_assign_domain3(cp, line, false);
+ free(cp);
ccs_put();
}
}
*/
static int ccs_domainname_attribute_compare(const void *a, const void *b)
{
- const struct ccs_domain_info *a0 = a;
- const struct ccs_domain_info *b0 = b;
- const int k = strcmp(a0->domainname->name, b0->domainname->name);
- if ((k > 0) || (!k && !a0->is_dis && b0->is_dis))
- return 1;
+ const struct ccs_domain *a0 = a;
+ const struct ccs_domain *b0 = b;
+ char *name1;
+ char *name2;
+ char *line;
+ char *cp;
+ int k;
+ if (!a0->target && !b0->target)
+ return strcmp(a0->domainname->name, b0->domainname->name);
+ name1 = ccs_strdup(a0->domainname->name);
+ if (a0->target) {
+ cp = strrchr(name1, ' ');
+ if (cp)
+ *cp = '\0';
+ }
+ name2 = ccs_strdup(b0->domainname->name);
+ if (b0->target) {
+ cp = strrchr(name2, ' ');
+ if (cp)
+ *cp = '\0';
+ }
+ k = strcmp(name1, name2);
+ if (k)
+ goto done;
+ ccs_get();
+ if (a0->target)
+ line = ccs_shprintf("%s %s", name1, a0->target->name);
+ else
+ line = ccs_shprintf("%s", name1);
+ free(name1);
+ name1 = ccs_strdup(line);
+ if (b0->target)
+ line = ccs_shprintf("%s %s", name2, b0->target->name);
+ else
+ line = ccs_shprintf("%s", name2);
+ free(name2);
+ name2 = ccs_strdup(line);
+ ccs_put();
+ k = strcmp(name1, name2);
+done:
+ free(name1);
+ free(name2);
return k;
}
/**
- * ccs_find_target_domain - Find the initialize_domain target domain.
+ * ccs_find_target_domain - Find the domain jump target domain.
*
* @index: Index in the domain policy.
*
- * Returns index in the domain policy if found, EOF otherwise.
+ * Returns index of the domain if found in a current namespace,
+ * -2 if found in a different namespace, EOF otherwise.
*/
static int ccs_find_target_domain(const int index)
{
- return ccs_find_domain(&ccs_dp, ccs_dp.list[index].target_domainname,
- false, false);
+ const char *cp = ccs_dp.list[index].target->name;
+ if (!ccs_is_current_namespace(cp)) {
+ if (ccs_dp.list[index].is_du)
+ return EOF;
+ return -2;
+ }
+ return ccs_find_domain3(cp, NULL, false);
}
/**
const char *sp;
const int number = ccs_dp.list[index].number;
int redirect_index;
- const bool is_dis = ccs_initializer_source(index);
+ const bool is_djs = ccs_jump_source(index);
const bool is_deleted = ccs_deleted_domain(index);
- if (number >= 0) {
- printw("%c%4d:", ccs_dp.list_selected[index] ? '&' : ' ',
- number);
- if (ccs_dp.list[index].profile_assigned)
- printw("%3u", ccs_dp.list[index].profile);
- else
- printw("???");
- printw(" %c%c%c ", ccs_keeper_domain(index) ? '#' : ' ',
- ccs_initializer_target(index) ? '*' : ' ',
+ if (number >= 0)
+ printw("%c%4d:%3u %c%c%c ", ccs_dp.list_selected[index] ? '&' :
+ ' ', number, ccs_dp.list[index].profile,
+ ccs_keeper_domain(index) ? '#' : ' ',
+ ccs_jump_target(index) ? '*' : ' ',
ccs_domain_unreachable(index) ? '!' : ' ');
- } else
+ else if (ccs_dp.list[index].is_djt)
+ printw(" %c*%c ",
+ ccs_keeper_domain(index) ? '#' : ' ',
+ ccs_domain_unreachable(index) ? '!' : ' ');
+ else
printw(" ");
tmp_col += 14;
- sp = ccs_domain_name(&ccs_dp, index);
+ sp = ccs_dp.list[index].domainname->name;
while (true) {
const char *cp = strchr(sp, ' ');
if (!cp)
tmp_col += 4;
sp = cp + 1;
}
+ if (is_djs) {
+ printw("%s", ccs_eat("=> "));
+ tmp_col += 3;
+ sp = ccs_dp.list[index].target->name;
+ }
if (is_deleted) {
printw("%s", ccs_eat("( "));
tmp_col += 2;
tmp_col += 2;
}
transition_control = ccs_dp.list[index].d_t;
- if (!transition_control || is_dis)
+ if (!transition_control || is_djs)
goto no_transition_control;
ccs_get();
line = ccs_shprintf(" ( %s%s from %s )",
ccs_put();
goto done;
no_transition_control:
- if (!is_dis)
+ if (!is_djs)
goto done;
ccs_get();
redirect_index = ccs_find_target_domain(index);
if (redirect_index >= 0)
line = ccs_shprintf(" ( -> %d )",
ccs_dp.list[redirect_index].number);
- else
+ else if (redirect_index == EOF)
line = ccs_shprintf(" ( -> Not Found )");
+ else
+ line = ccs_shprintf(" ( -> Namespace jump )");
printw("%s", ccs_eat(line));
tmp_col += strlen(line);
ccs_put();
"position.\n");
}
switch (screen) {
+ case CCS_SCREEN_NS_LIST:
+ if (!readonly)
+ printw("A/a Add a new namespace.\n");
+ break;
case CCS_SCREEN_DOMAIN_LIST:
if (ccs_domain_sort_type) {
printw("S/s Set profile number of selected "
} else {
if (!readonly) {
printw("A/a Add a new domain.\n");
- printw("D/d Delete selected domains.\n");
+ printw("D/d Delete selected domains."
+ "\n");
printw("S/s Set profile number of "
"selected domains.\n");
}
{
if (filename) {
const int len = strlen(filename) + 128;
- ccs_last_error = realloc(ccs_last_error, len);
- if (!ccs_last_error)
- ccs_out_of_memory();
- memset(ccs_last_error, 0, len);
+ ccs_last_error = ccs_realloc2(ccs_last_error, len);
snprintf(ccs_last_error, len - 1, "Can't open %s .", filename);
} else {
free(ccs_last_error);
}
/**
- * ccs_send_fd - Send file descriptor.
- *
- * @data: String data to send with file descriptor.
- * @fd: Pointer to file desciptor.
- *
- * Returns nothing.
- */
-static void ccs_send_fd(char *data, int *fd)
-{
- struct msghdr msg;
- struct iovec iov = { data, strlen(data) };
- char cmsg_buf[CMSG_SPACE(sizeof(int))];
- struct cmsghdr *cmsg = (struct cmsghdr *) cmsg_buf;
- memset(&msg, 0, sizeof(msg));
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_control = cmsg_buf;
- msg.msg_controllen = sizeof(cmsg_buf);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- msg.msg_controllen = cmsg->cmsg_len;
- memmove(CMSG_DATA(cmsg), fd, sizeof(int));
- sendmsg(ccs_persistent_fd, &msg, 0);
- close(*fd);
-}
-
-/**
* ccs_editpolicy_open_write - Wrapper for ccs_open_write().
*
* @filename: File to open for writing.
*/
static FILE *ccs_editpolicy_open_write(const char *filename)
{
- if (ccs_network_mode) {
- FILE *fp = ccs_open_write(filename);
- if (!fp)
- ccs_set_error(filename);
- return fp;
- } else if (ccs_offline_mode) {
- char request[1024];
- int fd[2];
- if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd)) {
- fprintf(stderr, "socketpair()\n");
- exit(1);
- }
- if (shutdown(fd[0], SHUT_RD))
- goto out;
- memset(request, 0, sizeof(request));
- snprintf(request, sizeof(request) - 1, "POST %s", filename);
- ccs_send_fd(request, &fd[1]);
- return fdopen(fd[0], "w");
-out:
- close(fd[1]);
- close(fd[0]);
- exit(1);
- } else {
- FILE *fp;
- if (ccs_readonly_mode)
- return NULL;
- fp = ccs_open_write(filename);
- if (!fp)
- ccs_set_error(filename);
- return fp;
- }
+ FILE *fp = ccs_open_write(filename);
+ if (!fp)
+ ccs_set_error(filename);
+ return fp;
}
/**
*/
static FILE *ccs_editpolicy_open_read(const char *filename)
{
- if (ccs_network_mode) {
- return ccs_open_read(filename);
- } else if (ccs_offline_mode) {
- char request[1024];
- int fd[2];
- FILE *fp;
- if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd)) {
- fprintf(stderr, "socketpair()\n");
- exit(1);
- }
- if (shutdown(fd[0], SHUT_WR))
- goto out;
- fp = fdopen(fd[0], "r");
- if (!fp)
- goto out;
- memset(request, 0, sizeof(request));
- snprintf(request, sizeof(request) - 1, "GET %s", filename);
- ccs_send_fd(request, &fd[1]);
- return fp;
-out:
- close(fd[1]);
- close(fd[0]);
- exit(1);
- } else {
- return fopen(filename, "r");
- }
+ FILE *fp = ccs_open_read(filename);
+ if (!fp)
+ ccs_set_error(filename);
+ return fp;
}
/**
/**
* ccs_transition_control - Find domain transition control.
*
- * @domainname: Pointer to "const struct ccs_path_info".
+ * @ns: Pointer to "const struct ccs_path_info".
+ * @domainname: Domainname.
* @program: Program name.
*
* Returns pointer to "const struct ccs_transition_control_entry" if found one,
* NULL otherwise.
*/
static const struct ccs_transition_control_entry *ccs_transition_control
-(const struct ccs_path_info *domainname, const char *program)
+(const struct ccs_path_info *ns, const char *domainname, const char *program)
{
int i;
u8 type;
+ struct ccs_path_info domain;
struct ccs_path_info last_name;
- last_name.name = strrchr(domainname->name, ' ');
- if (last_name.name)
- last_name.name++;
- else
- last_name.name = domainname->name;
+ domain.name = domainname;
+ last_name.name = ccs_get_last_word(domainname);
+ ccs_fill_path_info(&domain);
ccs_fill_path_info(&last_name);
for (type = 0; type < CCS_MAX_TRANSITION_TYPE; type++) {
next:
= &ccs_transition_control_list[i];
if (ptr->type != type)
continue;
- if (ptr->domainname) {
- if (!ptr->is_last_name) {
- if (ccs_pathcmp(ptr->domainname,
- domainname))
- continue;
- } else {
- if (ccs_pathcmp(ptr->domainname,
- &last_name))
- continue;
- }
- }
+ if (ccs_pathcmp(ptr->ns, ns))
+ continue;
+ if (ptr->domainname &&
+ ccs_pathcmp(ptr->domainname, &domain) &&
+ ccs_pathcmp(ptr->domainname, &last_name))
+ continue;
if (ptr->program &&
strcmp(ptr->program->name, program))
continue;
+ if (type == CCS_TRANSITION_CONTROL_NO_RESET) {
+ /*
+ * Do not check for reset_domain if
+ * no_reset_domain matched.
+ */
+ type = CCS_TRANSITION_CONTROL_NO_INITIALIZE;
+ goto next;
+ }
if (type == CCS_TRANSITION_CONTROL_NO_INITIALIZE) {
/*
* Do not check for initialize_domain if
type = CCS_TRANSITION_CONTROL_NO_KEEP;
goto next;
}
- if (type == CCS_TRANSITION_CONTROL_INITIALIZE ||
+ if (type == CCS_TRANSITION_CONTROL_RESET ||
+ type == CCS_TRANSITION_CONTROL_INITIALIZE ||
type == CCS_TRANSITION_CONTROL_KEEP)
return ptr;
else
const int a2 = a0->directive;
const int b2 = b0->directive;
if (a2 >= 256 || b2 >= 256) {
- int i;
- static const char *global[5] = {
- "PROFILE_VERSION=",
- "PREFERENCE::audit=",
- "PREFERENCE::learning=",
- "PREFERENCE::permissive=",
- "PREFERENCE::enforcing="
- };
- for (i = 0; i < 5; i++) {
- if (!strncmp(a1, global[i], strlen(global[i])))
- return -1;
- if (!strncmp(b1, global[i], strlen(global[i])))
- return 1;
- }
+ if (a1[0] == 'P')
+ return -1;
+ if (b1[0] == 'P')
+ return 1;
}
if (!ccs_profile_sort_type) {
if (a2 == b2)
}
/**
+ * ccs_add_generic_entry - Add text lines.
+ *
+ * @line: Line to add.
+ * @directive: One of values in "enum ccs_editpolicy_directives".
+ *
+ * Returns true if this line deals current namespace, false otherwise.
+ */
+static void ccs_add_generic_entry(const char *line, const enum
+ ccs_editpolicy_directives directive)
+{
+ int i;
+ for (i = 0; i < ccs_gacl_list_count; i++)
+ if (ccs_gacl_list[i].directive == directive &&
+ !strcmp(line, ccs_gacl_list[i].operand))
+ return;
+ i = ccs_gacl_list_count++;
+ ccs_gacl_list = ccs_realloc(ccs_gacl_list, ccs_gacl_list_count *
+ sizeof(struct ccs_generic_acl));
+ ccs_gacl_list[i].directive = directive;
+ ccs_gacl_list[i].selected = 0;
+ ccs_gacl_list[i].operand = ccs_strdup(line);
+}
+
+/**
* ccs_read_generic_policy - Read policy data other than domain policy.
*
* Returns nothing.
{
FILE *fp = NULL;
_Bool flag = false;
+ const _Bool is_kernel_ns = !strcmp(ccs_current_ns->name, "<kernel>");
while (ccs_gacl_list_count)
- free((void *)
- ccs_gacl_list[--ccs_gacl_list_count].
- operand);
+ free((void *) ccs_gacl_list[--ccs_gacl_list_count].operand);
if (ccs_current_screen == CCS_SCREEN_ACL_LIST) {
if (ccs_network_mode)
/* We can read after write. */
fp = ccs_editpolicy_open_write(ccs_policy_file);
- else if (!ccs_offline_mode)
+ else
/* Don't set error message if failed. */
fp = fopen(ccs_policy_file, "r+");
if (fp) {
fputc(0, fp);
fflush(fp);
}
+ } else if (ccs_current_screen == CCS_SCREEN_NS_LIST) {
+ ccs_add_generic_entry("<kernel>", CCS_DIRECTIVE_NONE);
}
if (!fp)
fp = ccs_editpolicy_open_read(ccs_policy_file);
if (!line[0])
continue;
}
+ if (ccs_current_screen == CCS_SCREEN_EXCEPTION_LIST ||
+ ccs_current_screen == CCS_SCREEN_PROFILE_LIST) {
+ if (*line == '<') {
+ cp = strchr(line, ' ');
+ if (!cp++ || !ccs_is_current_namespace(line))
+ continue;
+ memmove(line, cp, strlen(cp) + 1);
+ } else if (!is_kernel_ns)
+ continue;
+ }
switch (ccs_current_screen) {
case CCS_SCREEN_EXCEPTION_LIST:
+ directive = ccs_find_directive(true, line);
+ if (directive == CCS_DIRECTIVE_NONE)
+ continue;
+ /* Remember groups for ccs_editpolicy_optimize(). */
+ if (directive != CCS_DIRECTIVE_PATH_GROUP &&
+ directive != CCS_DIRECTIVE_NUMBER_GROUP &&
+ directive != CCS_DIRECTIVE_ADDRESS_GROUP)
+ break;
+ cp = ccs_strdup(line);
+ if (directive == CCS_DIRECTIVE_PATH_GROUP)
+ ccs_add_path_group_policy(ccs_current_ns, cp,
+ false);
+ else if (directive == CCS_DIRECTIVE_NUMBER_GROUP)
+ ccs_add_number_group_policy(cp, false);
+ else
+ ccs_add_address_group_policy(cp, false);
+ free(cp);
+ break;
case CCS_SCREEN_ACL_LIST:
directive = ccs_find_directive(true, line);
if (directive == CCS_DIRECTIVE_NONE)
} else
directive = (u16) -1;
break;
+ case CCS_SCREEN_NS_LIST:
+ if (*line != '<')
+ continue;
+ cp = strchr(line, ' ');
+ if (!cp)
+ continue;
+ *cp = '\0';
+ if (!ccs_domain_def(line))
+ continue;
+ /* Fall through. */
default:
directive = CCS_DIRECTIVE_NONE;
break;
}
- ccs_gacl_list =
- realloc(ccs_gacl_list,
- (ccs_gacl_list_count + 1) *
- sizeof(struct ccs_generic_acl));
- if (!ccs_gacl_list)
- ccs_out_of_memory();
- cp = strdup(line);
- if (!cp)
- ccs_out_of_memory();
- ccs_gacl_list[ccs_gacl_list_count].directive =
- directive;
- ccs_gacl_list[ccs_gacl_list_count].selected = 0;
- ccs_gacl_list[ccs_gacl_list_count++].operand =
- cp;
+ ccs_add_generic_entry(line, directive);
}
ccs_put();
ccs_freadline_raw = false;
}
/**
- * ccs_add_transition_control_entry - Add "initialize_domain"/"no_initialize_domain"/"keep_domain"/ "no_keep_domain" entries.
+ * ccs_add_transition_control_entry - Add "reset_domain"/"no_reset_domain"/"initialize_domain"/"no_initialize_domain"/"keep_domain"/ "no_keep_domain" entries.
*
+ * @ns: Pointer to "const struct ccs_path_info".
* @domainname: Domainname.
* @program: Program name.
- * @type: One of values in "enum ccs_transition_type".
+ * @type: One of values in "enum ccs_transition_type".
*
* Returns 0 on success, -EINVAL otherwise.
*/
static int ccs_add_transition_control_entry
-(const char *domainname, const char *program,
+(const struct ccs_path_info *ns, const char *domainname, const char *program,
const enum ccs_transition_type type)
{
- void *vp;
struct ccs_transition_control_entry *ptr;
- _Bool is_last_name = false;
- if (program && strcmp(program, "any")) {
+ if (program && strcmp(program, "any"))
if (!ccs_correct_path(program))
return -EINVAL;
- }
- if (domainname && strcmp(domainname, "any")) {
- if (!ccs_correct_domain(domainname)) {
+ if (domainname && strcmp(domainname, "any"))
+ if (!ccs_correct_domain(domainname))
if (!ccs_correct_path(domainname))
return -EINVAL;
- is_last_name = true;
- }
- }
- vp = realloc(ccs_transition_control_list,
- (ccs_transition_control_list_len + 1) *
- sizeof(struct ccs_transition_control_entry));
- if (!vp)
- ccs_out_of_memory();
- ccs_transition_control_list = vp;
+ ccs_transition_control_list =
+ ccs_realloc(ccs_transition_control_list,
+ (ccs_transition_control_list_len + 1) *
+ sizeof(struct ccs_transition_control_entry));
ptr = &ccs_transition_control_list[ccs_transition_control_list_len++];
- memset(ptr, 0, sizeof(struct ccs_transition_control_entry));
- if (program && strcmp(program, "any")) {
+ memset(ptr, 0, sizeof(*ptr));
+ ptr->ns = ns;
+ if (program && strcmp(program, "any"))
ptr->program = ccs_savename(program);
- if (!ptr->program)
- ccs_out_of_memory();
- }
- if (domainname && strcmp(domainname, "any")) {
+ if (domainname && strcmp(domainname, "any"))
ptr->domainname = ccs_savename(domainname);
- if (!ptr->domainname)
- ccs_out_of_memory();
- }
ptr->type = type;
- ptr->is_last_name = is_last_name;
return 0;
}
/**
* ccs_add_path_group_entry - Add "path_group" entry.
*
+ * @ns: Pointer to "const struct ccs_path_info".
* @group_name: Name of address group.
* @member_name: Address string.
* @is_delete: True if it is delete request, false otherwise.
*
* Returns 0 on success, negative value otherwise.
*/
-static int ccs_add_path_group_entry(const char *group_name,
+static int ccs_add_path_group_entry(const struct ccs_path_info *ns,
+ const char *group_name,
const char *member_name,
const _Bool is_delete)
{
return -EINVAL;
saved_group_name = ccs_savename(group_name);
saved_member_name = ccs_savename(member_name);
- if (!saved_group_name || !saved_member_name)
- return -ENOMEM;
for (i = 0; i < ccs_path_group_list_len; i++) {
group = &ccs_path_group_list[i];
+ if (group->ns != ns)
+ continue;
if (saved_group_name != group->group_name)
continue;
for (j = 0; j < group->member_name_len; j++) {
if (is_delete)
return -ENOENT;
if (i == ccs_path_group_list_len) {
- ccs_path_group_list = realloc(ccs_path_group_list,
- (ccs_path_group_list_len + 1) *
- sizeof(struct ccs_path_group_entry));
- if (!ccs_path_group_list)
- ccs_out_of_memory();
+ ccs_path_group_list =
+ ccs_realloc(ccs_path_group_list,
+ (ccs_path_group_list_len + 1) *
+ sizeof(struct ccs_path_group_entry));
group = &ccs_path_group_list[ccs_path_group_list_len++];
- memset(group, 0, sizeof(struct ccs_path_group_entry));
+ memset(group, 0, sizeof(*group));
+ group->ns = ns;
group->group_name = saved_group_name;
}
- group->member_name = realloc(group->member_name,
- (group->member_name_len + 1)
- * sizeof(const struct ccs_path_info *));
- if (!group->member_name)
- ccs_out_of_memory();
+ group->member_name =
+ ccs_realloc(group->member_name, (group->member_name_len + 1) *
+ sizeof(const struct ccs_path_info *));
group->member_name[group->member_name_len++] = saved_member_name;
return 0;
}
static char domainname[4096];
int source;
char *cp = strrchr(line, ' ');
- if (!cp || strncmp(cp, " auto_domain_transition=\"", 25))
+ if (!cp)
+ return;
+ if (strncmp(cp, " auto_domain_transition=\"", 25))
return;
*cp = '\0';
cp += 25;
if (!source)
return;
cp[source - 1] = '\0';
- snprintf(domainname, sizeof(domainname) - 1, "%s %s",
- ccs_domain_name(&ccs_dp, index), cp);
+ snprintf(domainname, sizeof(domainname) - 1, "%s %s",
+ ccs_dp.list[index].domainname->name, cp);
domainname[sizeof(domainname) - 1] = '\0';
ccs_normalize_line(domainname);
- cp = strdup(domainname);
- ccs_jump_list = realloc(ccs_jump_list,
- (ccs_jump_list_len + 1) * sizeof(char *));
- if (!cp || !ccs_jump_list)
- ccs_out_of_memory();
- ccs_jump_list[ccs_jump_list_len++] = cp;
- source = ccs_assign_domain(&ccs_dp, domainname, true, false);
- if (source == EOF)
- ccs_out_of_memory();
- cp = strdup(domainname);
- if (!cp)
- ccs_out_of_memory();
- ccs_dp.list[source].target_domainname = cp;
+ ccs_jump_list = ccs_realloc(ccs_jump_list,
+ (ccs_jump_list_len + 1) * sizeof(char *));
+ ccs_jump_list[ccs_jump_list_len++] = ccs_strdup(domainname);
+ ccs_assign_domain3(domainname, *cp == '<' ? cp : domainname, false);
}
/**
static void ccs_add_acl_domain_transition(char *line, const int index)
{
static char domainname[4096];
- int source;
- char *cp;
- for (source = 0; line[source]; source++)
- if (line[source] == ' ' && line[source + 1] != '/') {
- line[source] = '\0';
+ int pos;
+ /* Chop off condition part which follows domainname. */
+ for (pos = 0; line[pos]; pos++)
+ if (line[pos] == ' ' && line[pos + 1] != '/') {
+ line[pos] = '\0';
break;
}
if (!ccs_correct_domain(line))
return;
- cp = strdup(line);
- ccs_jump_list = realloc(ccs_jump_list,
- (ccs_jump_list_len + 1) * sizeof(char *));
- if (!cp || !ccs_jump_list)
- ccs_out_of_memory();
- ccs_jump_list[ccs_jump_list_len++] = cp;
- cp = strrchr(line, ' ');
- if (cp)
- cp++;
- else
- cp = line;
- snprintf(domainname, sizeof(domainname) - 1, "%s %s",
- ccs_domain_name(&ccs_dp, index), cp);
+ ccs_jump_list = ccs_realloc(ccs_jump_list,
+ (ccs_jump_list_len + 1) * sizeof(char *));
+ ccs_jump_list[ccs_jump_list_len++] = ccs_strdup(line);
+ snprintf(domainname, sizeof(domainname) - 1, "%s %s",
+ ccs_dp.list[index].domainname->name, ccs_get_last_word(line));
domainname[sizeof(domainname) - 1] = '\0';
ccs_normalize_line(domainname);
- source = ccs_assign_domain(&ccs_dp, domainname, true, false);
- if (source == EOF)
- ccs_out_of_memory();
- cp = strdup(line);
- if (!cp)
- ccs_out_of_memory();
- ccs_dp.list[source].target_domainname = cp;
+ ccs_assign_domain3(domainname, line, false);
}
/**
* ccs_parse_domain_line - Parse an ACL entry in domain policy.
*
+ * @ns: Pointer to "const struct ccs_path_info".
* @line: Line to parse.
* @index: Current domain's index.
- * @parse_flags: True if parse use_profile and use_group lines, false otherwise.
+ * @parse_flags: True if parse use_profile and use_group lines, false
+ * otherwise.
*
* Returns nothing.
*/
-static void ccs_parse_domain_line(char *line, const int index,
- const bool parse_flags)
+static void ccs_parse_domain_line(const struct ccs_path_info *ns, char *line,
+ const int index, const bool parse_flags)
{
ccs_add_condition_domain_transition(line, index);
if (ccs_str_starts(line, "task auto_execute_handler ") ||
ccs_str_starts(line, "task denied_execute_handler ") ||
ccs_str_starts(line, "file execute ")) {
+ /* Chop off condition part which follows pathname. */
char *cp = strchr(line, ' ');
if (cp)
*cp = '\0';
if (*line == '@' || ccs_correct_path(line))
- ccs_add_string_entry(&ccs_dp, line, index);
- } else if (ccs_str_starts(line,
- "task auto_domain_transition ") ||
- ccs_str_starts(line,
- "task manual_domain_transition ")) {
+ ccs_add_string_entry3(line, index);
+ } else if (ccs_str_starts(line, "task auto_domain_transition ") ||
+ ccs_str_starts(line, "task manual_domain_transition ")) {
ccs_add_acl_domain_transition(line, index);
} else if (parse_flags) {
unsigned int profile;
- if (sscanf(line, "use_profile %u", &profile) == 1) {
+ if (sscanf(line, "use_profile %u", &profile) == 1)
ccs_dp.list[index].profile = (u8) profile;
- ccs_dp.list[index].profile_assigned = 1;
- } else if (sscanf(line, "use_group %u", &profile) == 1) {
+ else if (sscanf(line, "use_group %u", &profile) == 1)
ccs_dp.list[index].group = (u8) profile;
- }
}
}
/**
* ccs_parse_exception_line - Parse an ACL entry in exception policy.
*
- * @line: Line to parse.
- * @max_index: Number of domains currently defined.
+ * @ns: Pointer to "const struct ccs_path_info".
+ * @line: Line to parse.
*
* Returns nothing.
*/
-static void ccs_parse_exception_line(char *line, const int max_index)
+static void ccs_parse_exception_line(const struct ccs_path_info *ns,
+ char *line)
{
unsigned int group;
- if (ccs_str_starts(line, "initialize_domain "))
- ccs_add_transition_control_policy
- (line, CCS_TRANSITION_CONTROL_INITIALIZE);
- else if (ccs_str_starts(line, "no_initialize_domain "))
- ccs_add_transition_control_policy
- (line, CCS_TRANSITION_CONTROL_NO_INITIALIZE);
- else if (ccs_str_starts(line, "keep_domain "))
- ccs_add_transition_control_policy
- (line, CCS_TRANSITION_CONTROL_KEEP);
- else if (ccs_str_starts(line, "no_keep_domain "))
- ccs_add_transition_control_policy
- (line, CCS_TRANSITION_CONTROL_NO_KEEP);
- else if (ccs_str_starts(line, "path_group "))
- ccs_add_path_group_policy(line, false);
+ for (group = 0; group < CCS_MAX_TRANSITION_TYPE; group++) {
+ if (!ccs_str_starts(line, ccs_transition_type[group]))
+ continue;
+ ccs_add_transition_control_policy(ns, line, group);
+ return;
+ }
+ if (ccs_str_starts(line, "path_group "))
+ ccs_add_path_group_policy(ns, line, false);
else if (ccs_str_starts(line, "address_group "))
ccs_add_address_group_policy(line, false);
else if (ccs_str_starts(line, "number_group "))
ccs_add_number_group_policy(line, false);
else if (sscanf(line, "acl_group %u", &group) == 1 && group < 256) {
int index;
- char *cp = strchr(line + 10, ' ');
- if (cp)
- line = cp + 1;
- for (index = 0; index < max_index; index++) {
+ line = strchr(line + 10, ' ');
+ if (!line++)
+ return;
+ for (index = 0; index < ccs_dp.list_len; index++) {
+ char *cp;
if (ccs_dp.list[index].group != group)
continue;
- cp = strdup(line);
- if (!cp)
- ccs_out_of_memory();
- ccs_parse_domain_line(cp, index, false);
+ cp = ccs_strdup(line);
+ ccs_parse_domain_line(ns, cp, index, false);
free(cp);
}
}
*
* Returns nothing.
*
- * Since CUI policy editor screen shows domain initializer source domains and
+ * Since CUI policy editor screen shows domain jump source domains and
* unreachable domains, we need to read not only the domain policy but also
* the exception policy for printing the domain transition tree.
*/
int j;
int index;
int max_index;
+ static const struct ccs_path_info *ccs_kernel_ns = NULL;
+ const struct ccs_path_info *ns;
+
while (ccs_jump_list_len)
free(ccs_jump_list[--ccs_jump_list_len]);
- ccs_clear_domain_policy(&ccs_dp);
+ ccs_clear_domain_policy3();
ccs_transition_control_list_len = 0;
ccs_editpolicy_clear_groups();
- ccs_assign_domain(&ccs_dp, CCS_ROOT_NAME, false, false);
+ if (!ccs_kernel_ns)
+ ccs_kernel_ns = ccs_savename("<kernel>");
+ ns = ccs_kernel_ns;
/* Load all domain transition related entries. */
fp = NULL;
if (ccs_network_mode)
/* We can read after write. */
fp = ccs_editpolicy_open_write(CCS_PROC_POLICY_DOMAIN_POLICY);
- else if (!ccs_offline_mode)
+ else
/* Don't set error message if failed. */
fp = fopen(CCS_PROC_POLICY_DOMAIN_POLICY, "r+");
if (fp) {
char *line = ccs_freadline_unpack(fp);
if (!line)
break;
- if (ccs_domain_def(line)) {
- index = ccs_assign_domain(&ccs_dp, line, false,
- false);
+ if (*line == '<') {
+ ns = ccs_get_ns(line);
+ index = ccs_assign_domain3(line, NULL, false);
continue;
} else if (index == EOF) {
continue;
}
- ccs_parse_domain_line(line, index, true);
+ ccs_parse_domain_line(ns, line, index, true);
}
ccs_put();
fclose(fp);
- } else {
- ccs_set_error(CCS_PROC_POLICY_DOMAIN_POLICY);
}
- max_index = ccs_dp.list_len;
-
/* Load domain transition related entries and group entries. */
fp = NULL;
if (ccs_network_mode)
/* We can read after write. */
fp = ccs_editpolicy_open_write
(CCS_PROC_POLICY_EXCEPTION_POLICY);
- else if (!ccs_offline_mode)
+ else
/* Don't set error message if failed. */
fp = fopen(CCS_PROC_POLICY_EXCEPTION_POLICY, "r+");
if (fp) {
char *line = ccs_freadline_unpack(fp);
if (!line)
break;
- ccs_parse_exception_line(line, max_index);
+ if (*line == '<') {
+ char *cp = strchr(line, ' ');
+ if (!cp)
+ continue;
+ *cp++ = '\0';
+ ns = ccs_savename(line);
+ memmove(line, cp, strlen(cp) + 1);
+ } else
+ ns = ccs_kernel_ns;
+ ccs_parse_exception_line(ns, line);
}
ccs_put();
fclose(fp);
- } else {
- ccs_set_error(CCS_PROC_POLICY_EXCEPTION_POLICY);
}
/*
- * Find unreachable domains.
+ * Domain jump sources by "task manual_domain_transition" keyword or
+ * "task auto_domain_transition" keyword or "auto_domain_transition="
+ * part of conditional ACL have been created by now because these
+ * keywords do not depend on domain transition control directives
+ * defined in the exception policy.
*
- * This is calculated based on "initialize_domain" and "keep_domain"
- * keywords. However, since "task auto_domain_transition" and "task
- * manual_domain_transition" keywords and "auto_domain_transition="
- * condition are not subjected to "initialize_domain" and "keep_domain"
- * keywords, we need to adjust later.
+ * Create domain jump sources for "task auto_execute_handler" keyword
+ * or "task denied_execute_handler" keyword or "file execute" keyword
+ * now because these keywords depend on domain transition control
+ * directives defined in the exception policy. Note that "file execute"
+ * allows referring "path_group" directives.
*/
+ max_index = ccs_dp.list_len;
+ for (index = 0; index < max_index; index++) {
+ const char *domainname = ccs_dp.list[index].domainname->name;
+ const struct ccs_path_info **string_ptr
+ = ccs_dp.list[index].string_ptr;
+ const int max_count = ccs_dp.list[index].string_count;
+ /* Do not recursively create domain jump source. */
+ if (ccs_dp.list[index].target)
+ continue;
+ ns = ccs_get_ns(domainname);
+ for (i = 0; i < max_count; i++) {
+ const char *name = string_ptr[i]->name;
+ struct ccs_path_group_entry *group;
+ if (name[0] != '@') {
+ ccs_assign_djs(ns, domainname, name);
+ continue;
+ }
+ group = ccs_find_path_group_ns(ns, name + 1);
+ if (!group)
+ continue;
+ for (j = 0; j < group->member_name_len; j++) {
+ name = group->member_name[j]->name;
+ ccs_assign_djs(ns, domainname, name);
+ }
+ }
+ }
+
+ /* Create missing parent domains. */
+ max_index = ccs_dp.list_len;
for (index = 0; index < max_index; index++) {
char *line;
ccs_get();
- line = ccs_shprintf("%s", ccs_domain_name(&ccs_dp, index));
+ line = ccs_shprintf("%s", ccs_dp.list[index].domainname->name);
while (true) {
- const struct ccs_transition_control_entry *d_t;
- struct ccs_path_info parent;
char *cp = strrchr(line, ' ');
if (!cp)
break;
- *cp++ = '\0';
- parent.name = line;
- ccs_fill_path_info(&parent);
- d_t = ccs_transition_control(&parent, cp);
- if (!d_t)
- continue;
- /* Initializer under <kernel> is reachable. */
- if (d_t->type == CCS_TRANSITION_CONTROL_INITIALIZE &&
- parent.total_len == CCS_ROOT_NAME_LEN)
- break;
- ccs_dp.list[index].d_t = d_t;
- continue;
+ *cp = '\0';
+ if (ccs_find_domain3(line, NULL, false) == EOF)
+ ccs_assign_domain3(line, NULL, true);
}
ccs_put();
- if (ccs_dp.list[index].d_t)
- ccs_dp.list[index].is_du = true;
}
- /* Find domain initializer target domains. */
+ /*
+ * All domains and jump sources have been created by now.
+ * Let's markup domain jump targets and unreachable domains.
+ */
+ max_index = ccs_dp.list_len;
+
+ /*
+ * Find domains that might be reachable via
+ * "task manual_domain_transition" keyword or
+ * "task auto_domain_transition" keyword or
+ * "auto_domain_transition=" part of conditional ACL.
+ * Such domains are marked with '*'.
+ */
+ for (i = 0; i < ccs_jump_list_len; i++) {
+ struct ccs_domain *ptr =
+ ccs_find_domain3_by_name(ccs_jump_list[i]);
+ if (ptr)
+ ptr->is_djt = true;
+ }
+
+ /*
+ * Find domains that might be reachable via "initialize_domain"
+ * keyword. Such domains are marked with '*'.
+ */
for (index = 0; index < max_index; index++) {
- char *cp = strchr(ccs_domain_name(&ccs_dp, index), ' ');
- if (!cp || strchr(cp + 1, ' '))
+ const struct ccs_domain *domain = &ccs_dp.list[index];
+ const char *domainname = domain->domainname->name;
+ char *cp;
+ /* Ignore domain jump sources. */
+ if (domain->target)
+ continue;
+ /* Ignore if already marked as domain jump targets. */
+ if (domain->is_djt)
+ continue;
+ /* Ignore if not a namespace's root's child domain. */
+ cp = strchr(domainname, ' ');
+ if (!cp++ || strchr(cp, ' '))
+ continue;
+ /* Check "no_initialize_domain $program from any" entry. */
+ for (i = 0; i < ccs_transition_control_list_len; i++) {
+ struct ccs_transition_control_entry *ptr
+ = &ccs_transition_control_list[i];
+ if (ptr->type != CCS_TRANSITION_CONTROL_NO_INITIALIZE)
+ continue;
+ if (!ccs_is_same_namespace(domainname, ptr->ns))
+ continue;
+ if (ptr->domainname)
+ continue;
+ if (ptr->program && strcmp(ptr->program->name, cp))
+ continue;
+ break;
+ }
+ if (i < ccs_transition_control_list_len)
continue;
+ /*
+ * Check "initialize_domain $program from $domainname" entry.
+ */
for (i = 0; i < ccs_transition_control_list_len; i++) {
struct ccs_transition_control_entry *ptr
= &ccs_transition_control_list[i];
if (ptr->type != CCS_TRANSITION_CONTROL_INITIALIZE)
continue;
- if (ptr->program && strcmp(ptr->program->name, cp + 1))
+ if (!ccs_is_same_namespace(domainname, ptr->ns))
+ continue;
+ if (ptr->program && strcmp(ptr->program->name, cp))
continue;
- ccs_dp.list[index].is_dit = true;
+ break;
}
+ if (i < ccs_transition_control_list_len)
+ ccs_dp.list[index].is_djt = true;
}
- /* Find domain keeper domains. */
+ /*
+ * Find domains that might suppress domain transition via "keep_domain"
+ * keyword. Such domains are marked with '#'.
+ */
for (index = 0; index < max_index; index++) {
+ const struct ccs_domain *domain = &ccs_dp.list[index];
+ const struct ccs_path_info *name = domain->domainname;
+ const char *last_name = ccs_get_last_word(name->name);
+ /* Ignore domain jump sources. */
+ if (domain->target)
+ continue;
+ /* Check "no_keep_domain any from $domainname" entry. */
for (i = 0; i < ccs_transition_control_list_len; i++) {
struct ccs_transition_control_entry *ptr
= &ccs_transition_control_list[i];
- char *cp;
- if (ptr->type != CCS_TRANSITION_CONTROL_KEEP)
+ if (ptr->type != CCS_TRANSITION_CONTROL_NO_KEEP)
continue;
- if (!ptr->is_last_name) {
- if (ptr->domainname &&
- ccs_pathcmp(ptr->domainname,
- ccs_dp.list[index].domainname))
- continue;
- ccs_dp.list[index].is_dk = true;
+ if (!ccs_is_same_namespace(name->name, ptr->ns))
continue;
- }
- cp = strrchr(ccs_dp.list[index].domainname->name,
- ' ');
- if (!cp || (ptr->domainname->name &&
- strcmp(ptr->domainname->name, cp + 1)))
+ if (ptr->program)
continue;
- ccs_dp.list[index].is_dk = true;
+ if (!ptr->domainname ||
+ !ccs_pathcmp(ptr->domainname, name) ||
+ !strcmp(ptr->domainname->name, last_name))
+ break;
}
- }
-
- /* Create domain initializer source domains. */
- for (index = 0; index < max_index; index++) {
- const struct ccs_path_info *domainname
- = ccs_dp.list[index].domainname;
- const struct ccs_path_info **string_ptr
- = ccs_dp.list[index].string_ptr;
- const int max_count = ccs_dp.list[index].string_count;
- /*
- * Don't create source domain under <kernel> because
- * they will become target domains.
- */
- if (domainname->total_len == CCS_ROOT_NAME_LEN)
+ if (i < ccs_transition_control_list_len)
continue;
- for (i = 0; i < max_count; i++) {
- const struct ccs_path_info *cp = string_ptr[i];
- struct ccs_path_group_entry *group;
- if (cp->name[0] != '@') {
- ccs_assign_dis(domainname, cp->name);
+ /* Check "keep_domain $program from $domainname" entry. */
+ for (i = 0; i < ccs_transition_control_list_len; i++) {
+ struct ccs_transition_control_entry *ptr
+ = &ccs_transition_control_list[i];
+ if (ptr->type != CCS_TRANSITION_CONTROL_KEEP)
continue;
- }
- group = ccs_find_path_group(cp->name + 1);
- if (!group)
+ if (!ccs_is_same_namespace(name->name, ptr->ns))
continue;
- for (j = 0; j < group->member_name_len; j++) {
- cp = group->member_name[j];
- ccs_assign_dis(domainname, cp->name);
- }
+ if (!ptr->domainname ||
+ !ccs_pathcmp(ptr->domainname, name) ||
+ !strcmp(ptr->domainname->name, last_name))
+ break;
}
+ if (i < ccs_transition_control_list_len)
+ ccs_dp.list[index].is_dk = true;
}
/*
- * Create domain jump target domains.
- * This may reset unreachable domains.
+ * Find unreachable domains. Such domains are marked with '!'.
+ * Unreachable domains are caused by one of "initialize_domain" keyword
+ * or "keep_domain" keyword or "reset_domain" keyword.
*/
- for (i = 0; i < ccs_jump_list_len; i++) {
- const int index = ccs_find_domain(&ccs_dp, ccs_jump_list[i],
- false, false);
- if (index == EOF)
- continue;
- ccs_dp.list[index].is_dit = true;
- ccs_dp.list[index].d_t = NULL;
- ccs_dp.list[index].is_du = false;
- }
-
- /* Create missing parent domains. */
for (index = 0; index < max_index; index++) {
char *line;
+ struct ccs_domain * const domain = &ccs_dp.list[index];
+ /*
+ * Mark domain jump source as unreachable if domain jump target
+ * does not exist. Note that such domains are not marked with
+ * '!'.
+ */
+ if (domain->target) {
+ if (ccs_find_domain3(domain->target->name, NULL,
+ false) == EOF)
+ domain->is_du = true;
+ continue;
+ }
+ /* Ignore if domain jump targets. */
+ if (domain->is_djt)
+ continue;
+ /* Ignore if deleted domain. */
+ if (domain->is_dd)
+ continue;
+ ns = ccs_get_ns(domain->domainname->name);
ccs_get();
- line = ccs_shprintf("%s", ccs_domain_name(&ccs_dp, index));
+ line = ccs_shprintf("%s", domain->domainname->name);
while (true) {
- char *cp = strrchr(line, ' ');
- if (!cp)
+ const struct ccs_domain *ptr =
+ ccs_find_domain3_by_name(line);
+ const struct ccs_transition_control_entry *d_t;
+ char *cp;
+ /* Stop traversal if current is domain jump target. */
+ if (ptr && ptr->is_djt)
break;
- *cp = '\0';
- if (ccs_find_domain(&ccs_dp, line, false, false)
- != EOF)
- continue;
- if (ccs_assign_domain(&ccs_dp, line, false, true)
- == EOF)
- ccs_out_of_memory();
+ cp = strrchr(line, ' ');
+ if (cp)
+ *cp++ = '\0';
+ else
+ break;
+ d_t = ccs_transition_control(ns, line, cp);
+ if (d_t)
+ domain->d_t = d_t;
}
ccs_put();
+ if (domain->d_t)
+ domain->is_du = true;
}
/* Sort by domain name. */
- qsort(ccs_dp.list, ccs_dp.list_len, sizeof(struct ccs_domain_info),
+ qsort(ccs_dp.list, ccs_dp.list_len, sizeof(struct ccs_domain),
ccs_domainname_attribute_compare);
+ /*
+ * Since this screen shows domain transition tree within current
+ * namespace, purge domains that are not in current namespace.
+ */
+ for (index = 0; index < ccs_dp.list_len; index++) {
+ int i;
+ if (ccs_is_current_namespace(ccs_dp.list[index].
+ domainname->name))
+ continue;
+ free(ccs_dp.list[index].string_ptr);
+ ccs_dp.list_len--;
+ for (i = index; i < ccs_dp.list_len; i++)
+ ccs_dp.list[i] = ccs_dp.list[i + 1];
+ index--;
+ }
+
/* Assign domain numbers. */
{
int number = 0;
ccs_unnumbered_domain_count = 0;
for (index = 0; index < ccs_dp.list_len; index++) {
if (ccs_deleted_domain(index) ||
- ccs_initializer_source(index)) {
+ ccs_jump_source(index)) {
ccs_dp.list[index].number = -1;
ccs_unnumbered_domain_count++;
} else {
}
}
- ccs_dp.list_selected = realloc(ccs_dp.list_selected, ccs_dp.list_len);
- if (ccs_dp.list_len && !ccs_dp.list_selected)
- ccs_out_of_memory();
- memset(ccs_dp.list_selected, 0, ccs_dp.list_len);
+ if (!ccs_dp.list_len)
+ return;
+ ccs_dp.list_selected = ccs_realloc2(ccs_dp.list_selected,
+ ccs_dp.list_len);
}
/**
const int index = ccs_editpolicy_get_current();
ccs_get();
ccs_eat_col = ptr->x;
- line = ccs_shprintf("%s",
- ccs_eat(ccs_domain_name(&ccs_dp, index)));
+ if (index >= 0) {
+ line = ccs_shprintf
+ ("%s", ccs_eat(ccs_dp.list[index].
+ domainname->name));
+ if (ccs_jump_source(index)) {
+ char *cp = strrchr(line, ' ');
+ if (cp)
+ *cp = '\0';
+ }
+ } else
+ line = ccs_shprintf("%s", ccs_current_ns->name);
+ if (ccs_window_width < strlen(line))
+ line[ccs_window_width] = '\0';
+ move(2, 0);
+ clrtoeol();
+ ccs_editpolicy_attr_change(A_REVERSE, true); /* add color */
+ printw("%s", line);
+ ccs_editpolicy_attr_change(A_REVERSE, false); /* add color */
+ ccs_put();
+ }
+ if (ccs_current_screen == CCS_SCREEN_EXCEPTION_LIST ||
+ ccs_current_screen == CCS_SCREEN_PROFILE_LIST) {
+ char *line;
+ ccs_get();
+ ccs_eat_col = ptr->x;
+ line = ccs_shprintf("%s", ccs_current_ns->name);
if (ccs_window_width < strlen(line))
line[ccs_window_width] = '\0';
move(2, 0);
if (ccs_current_screen == CCS_SCREEN_DOMAIN_LIST) {
if (!ccs_domain_sort_type) {
if (ccs_deleted_domain(index) ||
- ccs_initializer_source(index))
+ ccs_jump_source(index))
return false;
ccs_dp.list_selected[index] ^= 1;
} else {
(CCS_PROC_POLICY_DOMAIN_POLICY);
if (!fp)
return;
- for (i = 1; i < ccs_dp.list_len; i++) {
+ for (i = 0; i < ccs_dp.list_len; i++) {
if (!ccs_dp.list_selected[i])
continue;
fprintf(fp, "delete %s\n",
- ccs_domain_name(&ccs_dp, i));
+ ccs_dp.list[i].domainname->name);
}
ccs_close_write(fp);
} else {
int i;
+ const _Bool is_kernel_ns = !strcmp(ccs_current_ns->name,
+ "<kernel>");
FILE *fp = ccs_editpolicy_open_write(ccs_policy_file);
if (!fp)
return;
if (!ccs_gacl_list[i].selected)
continue;
directive = ccs_gacl_list[i].directive;
- fprintf(fp, "delete %s %s\n",
+ fprintf(fp, "delete %s %s %s\n",
+ ccs_current_screen == CCS_SCREEN_EXCEPTION_LIST
+ && !is_kernel_ns ? ccs_current_ns->name : "",
ccs_directives[directive].original,
ccs_gacl_list[i].operand);
}
{
FILE *fp;
char *line;
+ const _Bool is_kernel_ns = !strcmp(ccs_current_ns->name, "<kernel>");
#ifndef __GPET
ccs_editpolicy_attr_change(A_BOLD, true); /* add color */
line = ccs_readline(ccs_window_height - 1, 0, "Enter new entry> ",
case CCS_SCREEN_DOMAIN_LIST:
if (!ccs_correct_domain(line)) {
const int len = strlen(line) + 128;
- ccs_last_error = realloc(ccs_last_error, len);
- if (!ccs_last_error)
- ccs_out_of_memory();
- memset(ccs_last_error, 0, len);
+ ccs_last_error = ccs_realloc2(ccs_last_error, len);
snprintf(ccs_last_error, len - 1,
"%s is an invalid domainname.", line);
line[0] = '\0';
fprintf(fp, "select domain=%s\n", ccs_current_domain);
/* Fall through. */
case CCS_SCREEN_EXCEPTION_LIST:
+ if (ccs_current_screen == CCS_SCREEN_EXCEPTION_LIST &&
+ !is_kernel_ns)
+ fprintf(fp, "%s ", ccs_current_ns->name);
directive = ccs_find_directive(false, line);
if (directive != CCS_DIRECTIVE_NONE)
- fprintf(fp, "%s ",
- ccs_directives[directive].original);
+ fprintf(fp, "%s ", ccs_directives[directive].original);
break;
case CCS_SCREEN_PROFILE_LIST:
if (!strchr(line, '='))
- fprintf(fp, "%s-COMMENT=\n", line);
+ fprintf(fp, "%s %s-COMMENT=\n",
+ !is_kernel_ns ? ccs_current_ns->name : "",
+ line);
+ if (!is_kernel_ns)
+ fprintf(fp, "%s ", ccs_current_ns->name);
+ break;
+ case CCS_SCREEN_NS_LIST:
+ fprintf(fp, "%s PROFILE_VERSION=20100903\n", line);
+ line[0] = '\0';
break;
default:
break;
if (!ccs_dp.list_selected[index])
continue;
fprintf(fp, "select domain=%s\n" "use_profile %s\n",
- ccs_domain_name(&ccs_dp, index), line);
+ ccs_dp.list[index].domainname->name, line);
}
} else {
for (index = 0; index < ccs_task_list_len; index++) {
if (cp)
*cp = '\0';
directive = ccs_gacl_list[index].directive;
+ fprintf(fp, "%s ", ccs_current_ns->name);
if (directive < 256)
fprintf(fp, "%u-", directive);
fprintf(fp, "%s=%s\n", buf, line);
*
* @current: Index in the domain policy.
*
- * Returns true if next window is ACL list, false otherwise.
+ * Returns true if next window is ACL list or namespace list, false otherwise.
*/
static _Bool ccs_select_acl_window(const int current)
{
char *old_domain;
- if (ccs_current_screen != CCS_SCREEN_DOMAIN_LIST || current == EOF)
+ if (current == EOF)
+ return false;
+ if (ccs_current_screen == CCS_SCREEN_NS_LIST) {
+ const char *namespace = ccs_gacl_list[current].operand;
+ if (ccs_previous_screen == CCS_SCREEN_ACL_LIST &&
+ strcmp(ccs_current_ns->name, namespace))
+ ccs_previous_screen = CCS_SCREEN_DOMAIN_LIST;
+ ccs_current_ns = ccs_savename(namespace);
+ ccs_current_screen = ccs_previous_screen;
+ return true;
+ }
+ if (ccs_current_screen != CCS_SCREEN_DOMAIN_LIST)
return false;
ccs_current_pid = 0;
if (ccs_domain_sort_type) {
ccs_current_pid = ccs_task_list[current].pid;
- } else if (ccs_initializer_source(current)) {
+ } else if (ccs_jump_source(current)) {
struct ccs_screen *ptr = &ccs_screen[ccs_current_screen];
const int redirect_index = ccs_find_target_domain(current);
- if (redirect_index == EOF)
- return false;
- ptr->current = redirect_index - ptr->y;
- while (ptr->current < 0) {
- ptr->current++;
- ptr->y--;
+ if (redirect_index >= 0) {
+ ptr->current = redirect_index - ptr->y;
+ while (ptr->current < 0) {
+ ptr->current++;
+ ptr->y--;
+ }
+ ccs_show_list();
+ }
+ if (redirect_index == -2) {
+ const char *domainname =
+ ccs_dp.list[current].target->name;
+ ccs_current_ns = ccs_get_ns(domainname);
+ free(ccs_current_domain);
+ ccs_current_domain = ccs_strdup(domainname);
+ ccs_current_screen = CCS_SCREEN_DOMAIN_LIST;
+ ccs_force_move_cursor = true;
+ return true;
}
- ccs_show_list();
return false;
} else if (ccs_deleted_domain(current)) {
return false;
}
old_domain = ccs_current_domain;
if (ccs_domain_sort_type)
- ccs_current_domain = strdup(ccs_task_list[current].domain);
+ ccs_current_domain = ccs_strdup(ccs_task_list[current].domain);
else
- ccs_current_domain = strdup(ccs_domain_name(&ccs_dp, current));
- if (!ccs_current_domain)
- ccs_out_of_memory();
+ ccs_current_domain = ccs_strdup(ccs_dp.list[current].
+ domainname->name);
ccs_no_restore_cursor = old_domain &&
strcmp(old_domain, ccs_current_domain);
free(old_domain);
+ ccs_current_screen = CCS_SCREEN_ACL_LIST;
return true;
}
printw("e <<< Exception Policy Editor >>>\n");
printw("d <<< Domain Transition Editor >>>\n");
if (ccs_current_screen == CCS_SCREEN_DOMAIN_LIST && current != EOF &&
- !ccs_initializer_source(current) && !ccs_deleted_domain(current))
+ !ccs_jump_source(current) && !ccs_deleted_domain(current))
printw("a <<< Domain Policy Editor >>>\n");
printw("p <<< Profile Editor >>>\n");
printw("m <<< Manager Policy Editor >>>\n");
+ printw("n <<< Namespace Selector >>>\n");
if (!ccs_offline_mode) {
/* printw("i <<< Interactive Enforcing Mode >>>\n"); */
printw("s <<< Statistics >>>\n");
return CCS_SCREEN_DOMAIN_LIST;
if (c == 'A' || c == 'a')
if (ccs_select_acl_window(current))
- return CCS_SCREEN_ACL_LIST;
+ return ccs_current_screen;
if (c == 'P' || c == 'p')
return CCS_SCREEN_PROFILE_LIST;
if (c == 'M' || c == 'm')
return CCS_SCREEN_MANAGER_LIST;
+ if (c == 'N' || c == 'n') {
+ ccs_previous_screen = ccs_current_screen;
+ return CCS_SCREEN_NS_LIST;
+ }
if (!ccs_offline_mode) {
/*
if (c == 'I' || c == 'i')
} else {
const u8 selected = ccs_dp.list_selected[current];
if (ccs_deleted_domain(current) ||
- ccs_initializer_source(current))
+ ccs_jump_source(current))
return;
for (index = current; index < ccs_dp.list_len;
index++) {
if (ccs_deleted_domain(index) ||
- ccs_initializer_source(index))
+ ccs_jump_source(index))
continue;
ccs_dp.list_selected[index] = selected;
}
switch (ccs_current_screen) {
enum ccs_editpolicy_directives directive;
case CCS_SCREEN_DOMAIN_LIST:
- line = ccs_domain_name(&ccs_dp, current);
+ if (!ccs_domain_sort_type) {
+ const struct ccs_domain *domain =
+ &ccs_dp.list[current];
+ if (domain->target)
+ line = domain->target->name;
+ else
+ line = domain->domainname->name;
+ } else
+ line = ccs_task_list[current].domain;
break;
case CCS_SCREEN_EXCEPTION_LIST:
case CCS_SCREEN_ACL_LIST:
} else if (ccs_current_screen == CCS_SCREEN_ACL_LIST) {
ccs_policy_file = CCS_PROC_POLICY_DOMAIN_POLICY;
ccs_list_caption = "Domain Policy Editor";
+ /*
} else if (ccs_current_screen == CCS_SCREEN_QUERY_LIST) {
ccs_policy_file = CCS_PROC_POLICY_QUERY;
ccs_list_caption = "Interactive Enforcing Mode";
+ */
+ } else if (ccs_current_screen == CCS_SCREEN_NS_LIST) {
+ ccs_policy_file = CCS_PROC_POLICY_PROFILE;
+ ccs_list_caption = "Namespace Selector";
} else if (ccs_current_screen == CCS_SCREEN_PROFILE_LIST) {
ccs_policy_file = CCS_PROC_POLICY_PROFILE;
ccs_list_caption = "Profile Editor";
/* ccs_list_caption = "Domain Transition Editor"; */
}
ptr = &ccs_screen[ccs_current_screen];
- if (ccs_no_restore_cursor) {
+ if (ccs_no_restore_cursor || ccs_force_move_cursor) {
ptr->current = 0;
ptr->y = 0;
ccs_no_restore_cursor = false;
if (ccs_current_screen == CCS_SCREEN_DOMAIN_LIST) {
if (!ccs_domain_sort_type) {
ccs_read_domain_and_exception_policy();
+ if (ccs_force_move_cursor) {
+ const int redirect_index =
+ ccs_find_domain3(ccs_current_domain,
+ NULL, false);
+ if (redirect_index >= 0) {
+ ptr->current = redirect_index - ptr->y;
+ while (ptr->current < 0) {
+ ptr->current++;
+ ptr->y--;
+ }
+ }
+ ccs_force_move_cursor = false;
+ }
ccs_adjust_cursor_pos(ccs_dp.list_len);
} else {
ccs_read_process_list(true);
case CCS_SCREEN_ACL_LIST:
case CCS_SCREEN_PROFILE_LIST:
case CCS_SCREEN_MANAGER_LIST:
+ case CCS_SCREEN_NS_LIST:
ccs_add_entry();
goto start;
default:
case '\r':
case '\n':
if (ccs_select_acl_window(current))
- return CCS_SCREEN_ACL_LIST;
+ return ccs_current_screen;
break;
case 's':
case 'S':
break;
case 'o':
case 'O':
- if (ccs_current_screen == CCS_SCREEN_ACL_LIST) {
+ if (ccs_current_screen == CCS_SCREEN_ACL_LIST ||
+ ccs_current_screen == CCS_SCREEN_EXCEPTION_LIST) {
ccs_editpolicy_optimize(current);
ccs_show_list();
}
{
FILE *proc_fp = ccs_editpolicy_open_read(src);
FILE *file_fp = fopen(dest, "w");
- if (!file_fp) {
+ int c;
+ if (!file_fp || !proc_fp) {
fprintf(stderr, "Can't open %s\n", dest);
- fclose(proc_fp);
+ if (file_fp)
+ fclose(file_fp);
+ if (proc_fp)
+ fclose(proc_fp);
return false;
}
while (true) {
- int c = fgetc(proc_fp);
- if (c == EOF)
+ c = fgetc(proc_fp);
+ if (!c || c == EOF)
+ break;
+ if (fputc(c, file_fp) == EOF) {
+ c = EOF;
break;
- fputc(c, file_fp);
+ }
}
fclose(proc_fp);
fclose(file_fp);
- return true;
+ return !c;
}
/**
goto usage;
ccs_policy_dir = ptr;
ccs_offline_mode = true;
+ } else if (*ptr == '<') {
+ if (ccs_current_ns || strchr(ptr, ' ') ||
+ !ccs_domain_def(ptr))
+ goto usage;
+ ccs_current_ns = ccs_savename(ptr);
} else if (cp) {
*cp++ = '\0';
if (ccs_network_mode || ccs_offline_mode)
ccs_current_screen = CCS_SCREEN_MANAGER_LIST;
else if (!strcmp(ptr, "s"))
ccs_current_screen = CCS_SCREEN_STAT_LIST;
+ else if (!strcmp(ptr, "n"))
+ ccs_current_screen = CCS_SCREEN_NS_LIST;
else if (!strcmp(ptr, "readonly"))
ccs_readonly_mode = true;
else if (sscanf(ptr, "refresh=%u", &ccs_refresh_interval)
!= 1) {
usage:
- printf("Usage: %s [e|d|p|m|s] [readonly] "
- "[refresh=interval] "
+ printf("Usage: %s [e|d|p|m|s|n] [readonly] "
+ "[refresh=interval] [<namespace>]"
"[{policy_dir|remote_ip:remote_port}]\n",
argv[0]);
exit(1);
}
}
+ if (!ccs_current_ns)
+ ccs_current_ns = ccs_savename("<kernel>");
}
/**
*/
static void ccs_load_offline(void)
{
- int fd[2] = { EOF, EOF };
+ int pipe_fd[2] = { EOF, EOF };
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
+ struct sockaddr_in addr = { };
+ socklen_t size = sizeof(addr);
+ /*
+ * Use PF_INET socket as a method for communicating with child task
+ * so that we can use same method for child task and
+ * ccs-editpolicy-agent.
+ */
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (chdir(ccs_policy_dir) || chdir("policy/current/")) {
fprintf(stderr, "Directory %s/policy/current/ doesn't "
"exist.\n", ccs_policy_dir);
exit(1);
}
- if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd)) {
- fprintf(stderr, "socketpair()\n");
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) || listen(fd, 5)
+ || getsockname(fd, (struct sockaddr *) &addr, &size)) {
+ fprintf(stderr, "Can't create listener socket.\n");
+ exit(1);
+ }
+ ccs_network_ip = addr.sin_addr.s_addr;
+ ccs_network_port = addr.sin_port;
+ ccs_network_mode = true;
+ /*
+ * Use pipe as a notifier for termination.
+ *
+ * Sending signals by remembering child task's PID would be possible.
+ * But such approach will not work if main task exited unexpectedly
+ * (e.g. SIGKILL). Since pipe_fd[1] is guaranteed to be closed no
+ * matter how main task exits, pipe approach is more reliable for
+ * telling the child task to exit.
+ */
+ if (pipe(pipe_fd)) {
+ fprintf(stderr, "Can't create pipe.\n");
exit(1);
}
switch (fork()) {
case 0:
- close(fd[0]);
- ccs_persistent_fd = fd[1];
- ccs_editpolicy_offline_daemon();
- _exit(0);
+ if (close(pipe_fd[1]) ||
+ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK))
+ _exit(1);
+ ccs_editpolicy_offline_daemon(fd, pipe_fd[0]);
+ _exit(1);
case -1:
fprintf(stderr, "fork()\n");
exit(1);
}
- close(fd[1]);
- ccs_persistent_fd = fd[0];
+ if (close(fd) || close(pipe_fd[0]))
+ exit(1);
+ ccs_copy_file("profile.conf", CCS_PROC_POLICY_PROFILE);
ccs_copy_file("exception_policy.conf",
CCS_PROC_POLICY_EXCEPTION_POLICY);
ccs_copy_file("domain_policy.conf", CCS_PROC_POLICY_DOMAIN_POLICY);
- ccs_copy_file("profile.conf", CCS_PROC_POLICY_PROFILE);
ccs_copy_file("manager.conf", CCS_PROC_POLICY_MANAGER);
if (chdir("..")) {
fprintf(stderr, "Directory %s/policy/ doesn't exist.\n",
{
ccs_parse_args(argc, argv);
ccs_editpolicy_init_keyword_map();
- if (ccs_offline_mode) {
+ if (ccs_offline_mode)
ccs_load_offline();
- goto start;
- }
if (ccs_network_mode)
goto start;
if (chdir(CCS_PROC_POLICY_DIR)) {
timeout(1000);
}
ccs_rl.max = 20;
- ccs_rl.history = malloc(ccs_rl.max * sizeof(const char *));
+ ccs_rl.history = ccs_malloc(ccs_rl.max * sizeof(const char *));
while (ccs_current_screen < CCS_MAXSCREEN) {
ccs_resize_window();
ccs_current_screen = ccs_generic_list_loop();
#endif /* __GPET */
if (ccs_offline_mode && !ccs_readonly_mode)
ccs_save_offline();
- ccs_clear_domain_policy(&ccs_dp);
+ ccs_clear_domain_policy3();
return 0;
}
*
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*
- * Version: 1.8.1 2011/04/01
+ * Version: 1.8.2+ 2011/07/07
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License v2 as published by the
CCS_SCREEN_ACL_LIST,
CCS_SCREEN_PROFILE_LIST,
CCS_SCREEN_MANAGER_LIST,
- CCS_SCREEN_QUERY_LIST,
+ /* CCS_SCREEN_QUERY_LIST, */
+ CCS_SCREEN_NS_LIST,
CCS_SCREEN_STAT_LIST,
CCS_MAXSCREEN
};
enum ccs_transition_type {
/* Do not change this order, */
+ CCS_TRANSITION_CONTROL_NO_RESET,
+ CCS_TRANSITION_CONTROL_RESET,
CCS_TRANSITION_CONTROL_NO_INITIALIZE,
CCS_TRANSITION_CONTROL_INITIALIZE,
CCS_TRANSITION_CONTROL_NO_KEEP,
CCS_DIRECTIVE_NETWORK_UNIX,
CCS_DIRECTIVE_NO_INITIALIZE_DOMAIN,
CCS_DIRECTIVE_NO_KEEP_DOMAIN,
+ CCS_DIRECTIVE_NO_RESET_DOMAIN,
CCS_DIRECTIVE_NUMBER_GROUP,
CCS_DIRECTIVE_PATH_GROUP,
CCS_DIRECTIVE_QUOTA_EXCEEDED,
+ CCS_DIRECTIVE_RESET_DOMAIN,
CCS_DIRECTIVE_TASK_AUTO_DOMAIN_TRANSITION,
CCS_DIRECTIVE_TASK_AUTO_EXECUTE_HANDLER,
CCS_DIRECTIVE_TASK_DENIED_EXECUTE_HANDLER,
CCS_MANAGER_CURSOR,
CCS_STAT_HEAD,
CCS_STAT_CURSOR,
+ CCS_DEFAULT_COLOR,
CCS_DISP_ERR
};
struct ccs_transition_control_entry {
+ const struct ccs_path_info *ns;
const struct ccs_path_info *domainname; /* This may be NULL */
const struct ccs_path_info *program; /* This may be NULL */
u8 type;
- _Bool is_last_name;
};
struct ccs_generic_acl {
};
struct ccs_path_group_entry {
+ const struct ccs_path_info *ns;
const struct ccs_path_info *group_name;
const struct ccs_path_info **member_name;
int member_name_len;
void ccs_editpolicy_color_init(void);
void ccs_editpolicy_init_keyword_map(void);
void ccs_editpolicy_line_draw(void);
-void ccs_editpolicy_offline_daemon(void);
+void ccs_editpolicy_offline_daemon(const int listener, const int notifier);
void ccs_editpolicy_optimize(const int current);
void ccs_editpolicy_sttr_restore(void);
void ccs_editpolicy_sttr_save(void);
+struct ccs_path_group_entry *ccs_find_path_group_ns
+(const struct ccs_path_info *ns, const char *group_name);
+
+struct ccs_domain {
+ const struct ccs_path_info *domainname;
+ const struct ccs_path_info *target; /* This may be NULL */
+ const struct ccs_transition_control_entry *d_t; /* This may be NULL */
+ const struct ccs_path_info **string_ptr;
+ int string_count;
+ int number; /* domain number (-1 if target or is_dd) */
+ u8 profile;
+ u8 group;
+ _Bool is_djt; /* domain jump target */
+ _Bool is_dk; /* domain keeper */
+ _Bool is_du; /* unreachable domain */
+ _Bool is_dd; /* deleted domain */
+};
+
+struct ccs_domain_policy3 {
+ struct ccs_domain *list;
+ int list_len;
+ unsigned char *list_selected;
+};
extern enum ccs_screen_type ccs_current_screen;
-extern int ccs_gacl_list_count;
extern int ccs_list_item_count;
extern int ccs_path_group_list_len;
-extern int ccs_persistent_fd;
-extern struct ccs_domain_policy ccs_dp;
+extern struct ccs_domain_policy3 ccs_dp;
extern struct ccs_editpolicy_directive ccs_directives[CCS_MAX_DIRECTIVE_INDEX];
extern struct ccs_generic_acl *ccs_gacl_list;
extern struct ccs_path_group_entry *ccs_path_group_list;
extern struct ccs_screen ccs_screen[CCS_MAXSCREEN];
+extern const struct ccs_path_info *ccs_current_ns;
#ifdef __GPET
#include "../g_undef.h"
*
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*
- * Version: 1.8.1 2011/04/01
+ * Version: 1.8.2 2011/06/20
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License v2 as published by the
COLOR_YELLOW, "STAT_HEAD" },
{ CCS_STAT_CURSOR, COLOR_BLACK,
COLOR_YELLOW, "STAT_CURSOR" },
+ { CCS_DEFAULT_COLOR, COLOR_WHITE,
+ COLOR_BLACK, "DEFAULT_COLOR" },
{ CCS_NORMAL, COLOR_WHITE,
COLOR_BLACK, NULL }
};
init_pair(colorp->tag, colorp->fore, colorp->back);
}
init_pair(CCS_DISP_ERR, COLOR_RED, COLOR_BLACK); /* error message */
+ bkgdset(A_NORMAL | COLOR_PAIR(CCS_DEFAULT_COLOR) | ' ');
for (i = 0; i < CCS_MAXSCREEN; i++)
ccs_screen[i].saved_color_current = -1;
}
*/
static void ccs_editpolicy_color_save(const _Bool flg)
{
- static attr_t save_color = CCS_NORMAL;
+ static attr_t save_color = CCS_DEFAULT_COLOR;
if (flg)
save_color = getattrs(stdscr);
else
}
/**
- * ccseditpolicy_color_head - Get color to use for header line.
+ * ccs_editpolicy_color_head - Get color to use for header line.
*
* Returns one of values in "enum ccs_color_pair".
*/
if (-1 < ptr->saved_color_current &&
current != ptr->saved_color_current) {
move(CCS_HEADER_LINES + ptr->saved_color_y, 0);
- chgat(-1, A_NORMAL, CCS_NORMAL, NULL);
+ chgat(-1, A_NORMAL, CCS_DEFAULT_COLOR, NULL);
}
move(y, x);
}
/**
- * ccseditpolicy_color_head - Get color to use for header line.
+ * ccs_editpolicy_color_head - Get color to use for header line.
*
* Returns one of values in "enum ccs_color_pair".
*/
enum ccs_color_pair ccs_editpolicy_color_head(void)
{
- return CCS_NORMAL;
+ return CCS_DEFAULT_COLOR;
}
/**
*
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*
- * Version: 1.8.1 2011/04/01
+ * Version: 1.8.2 2011/06/20
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License v2 as published by the
[CCS_DIRECTIVE_FILE_UNLINK] = { "file unlink", NULL, 0, 0 },
[CCS_DIRECTIVE_FILE_UNMOUNT] = { "file unmount", NULL, 0, 0 },
[CCS_DIRECTIVE_FILE_WRITE] = { "file write", NULL, 0, 0 },
- [CCS_DIRECTIVE_INITIALIZE_DOMAIN] = { "initialize_domain", NULL, 0, 0 },
+ [CCS_DIRECTIVE_INITIALIZE_DOMAIN]
+ = { "initialize_domain", NULL, 0, 0 },
[CCS_DIRECTIVE_IPC_SIGNAL] = { "ipc signal", NULL, 0, 0 },
[CCS_DIRECTIVE_KEEP_DOMAIN] = { "keep_domain", NULL, 0, 0 },
[CCS_DIRECTIVE_MISC_ENV] = { "misc env", NULL, 0, 0 },
[CCS_DIRECTIVE_NO_INITIALIZE_DOMAIN]
= { "no_initialize_domain", NULL, 0, 0 },
[CCS_DIRECTIVE_NO_KEEP_DOMAIN] = { "no_keep_domain", NULL, 0, 0 },
+ [CCS_DIRECTIVE_NO_RESET_DOMAIN] = { "no_reset_domain", NULL, 0, 0 },
[CCS_DIRECTIVE_NUMBER_GROUP] = { "number_group", NULL, 0, 0 },
[CCS_DIRECTIVE_PATH_GROUP] = { "path_group", NULL, 0, 0 },
[CCS_DIRECTIVE_QUOTA_EXCEEDED] = { "quota_exceeded", NULL, 0, 0 },
+ [CCS_DIRECTIVE_RESET_DOMAIN] = { "reset_domain", NULL, 0, 0 },
[CCS_DIRECTIVE_TASK_AUTO_DOMAIN_TRANSITION]
= { "task auto_domain_transition", NULL, 0, 0 },
[CCS_DIRECTIVE_TASK_AUTO_EXECUTE_HANDLER]
= { "task denied_execute_handler", NULL, 0, 0 },
[CCS_DIRECTIVE_TASK_MANUAL_DOMAIN_TRANSITION]
= { "task manual_domain_transition", NULL, 0, 0 },
- [CCS_DIRECTIVE_TRANSITION_FAILED] = { "transition_failed", NULL, 0, 0 },
+ [CCS_DIRECTIVE_TRANSITION_FAILED]
+ = { "transition_failed", NULL, 0, 0 },
[CCS_DIRECTIVE_USE_GROUP] = { "use_group", NULL, 0, 0 },
[CCS_DIRECTIVE_USE_PROFILE] = { "use_profile", NULL, 0, 0 },
};
if (strcmp(line, ccs_directives[i].original))
continue;
free((void *) ccs_directives[i].alias);
- cp = strdup(cp);
- if (!cp)
- ccs_out_of_memory();
+ cp = ccs_strdup(cp);
ccs_directives[i].alias = cp;
ccs_directives[i].alias_len = strlen(cp);
break;
*
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*
- * Version: 1.8.1 2011/04/01
+ * Version: 1.8.2+ 2011/07/13
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License v2 as published by the
*/
#include "ccstools.h"
#include "editpolicy.h"
+#include <poll.h>
+
+struct list_head {
+ struct list_head *next;
+ struct list_head *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)
+
+#ifndef offsetof
+#ifdef __compiler_offsetof
+#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
+#else
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+#endif
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+#define list_entry(ptr, type, member) container_of(ptr, type, member)
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+static inline void __list_add(struct list_head *new, struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+static inline int list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
+/* Enumeration definition for internal use. */
+
+/* Index numbers for Access Controls. */
+enum ccs_acl_entry_type_index {
+ CCS_TYPE_PATH_ACL,
+ CCS_TYPE_PATH2_ACL,
+ CCS_TYPE_PATH_NUMBER_ACL,
+ CCS_TYPE_MKDEV_ACL,
+ CCS_TYPE_MOUNT_ACL,
+ CCS_TYPE_ENV_ACL,
+ CCS_TYPE_CAPABILITY_ACL,
+ CCS_TYPE_INET_ACL,
+ CCS_TYPE_UNIX_ACL,
+ CCS_TYPE_SIGNAL_ACL,
+ CCS_TYPE_AUTO_EXECUTE_HANDLER,
+ CCS_TYPE_DENIED_EXECUTE_HANDLER,
+ CCS_TYPE_AUTO_TASK_ACL,
+ CCS_TYPE_MANUAL_TASK_ACL,
+};
+
+/* Index numbers for Capability Controls. */
+enum ccs_capability_acl_index {
+ /* socket(PF_ROUTE, *, *) */
+ CCS_USE_ROUTE_SOCKET,
+ /* socket(PF_PACKET, *, *) */
+ CCS_USE_PACKET_SOCKET,
+ /* sys_reboot() */
+ CCS_SYS_REBOOT,
+ /* sys_vhangup() */
+ CCS_SYS_VHANGUP,
+ /* do_settimeofday(), sys_adjtimex() */
+ CCS_SYS_SETTIME,
+ /* sys_nice(), sys_setpriority() */
+ CCS_SYS_NICE,
+ /* sys_sethostname(), sys_setdomainname() */
+ CCS_SYS_SETHOSTNAME,
+ /* sys_create_module(), sys_init_module(), sys_delete_module() */
+ CCS_USE_KERNEL_MODULE,
+ /* sys_kexec_load() */
+ CCS_SYS_KEXEC_LOAD,
+ /* sys_ptrace() */
+ CCS_SYS_PTRACE,
+ CCS_MAX_CAPABILITY_INDEX
+};
+
+/* Index numbers for "struct ccs_condition". */
+enum ccs_conditions_index {
+ CCS_TASK_UID, /* current_uid() */
+ CCS_TASK_EUID, /* current_euid() */
+ CCS_TASK_SUID, /* current_suid() */
+ CCS_TASK_FSUID, /* current_fsuid() */
+ CCS_TASK_GID, /* current_gid() */
+ CCS_TASK_EGID, /* current_egid() */
+ CCS_TASK_SGID, /* current_sgid() */
+ CCS_TASK_FSGID, /* current_fsgid() */
+ CCS_TASK_PID, /* sys_getpid() */
+ CCS_TASK_PPID, /* sys_getppid() */
+ CCS_EXEC_ARGC, /* "struct linux_binprm *"->argc */
+ CCS_EXEC_ENVC, /* "struct linux_binprm *"->envc */
+ CCS_TYPE_IS_SOCKET, /* S_IFSOCK */
+ CCS_TYPE_IS_SYMLINK, /* S_IFLNK */
+ CCS_TYPE_IS_FILE, /* S_IFREG */
+ CCS_TYPE_IS_BLOCK_DEV, /* S_IFBLK */
+ CCS_TYPE_IS_DIRECTORY, /* S_IFDIR */
+ CCS_TYPE_IS_CHAR_DEV, /* S_IFCHR */
+ CCS_TYPE_IS_FIFO, /* S_IFIFO */
+ CCS_MODE_SETUID, /* S_ISUID */
+ CCS_MODE_SETGID, /* S_ISGID */
+ CCS_MODE_STICKY, /* S_ISVTX */
+ CCS_MODE_OWNER_READ, /* S_IRUSR */
+ CCS_MODE_OWNER_WRITE, /* S_IWUSR */
+ CCS_MODE_OWNER_EXECUTE, /* S_IXUSR */
+ CCS_MODE_GROUP_READ, /* S_IRGRP */
+ CCS_MODE_GROUP_WRITE, /* S_IWGRP */
+ CCS_MODE_GROUP_EXECUTE, /* S_IXGRP */
+ CCS_MODE_OTHERS_READ, /* S_IROTH */
+ CCS_MODE_OTHERS_WRITE, /* S_IWOTH */
+ CCS_MODE_OTHERS_EXECUTE, /* S_IXOTH */
+ CCS_TASK_TYPE, /* ((u8) task->ccs_flags) &
+ CCS_TASK_IS_EXECUTE_HANDLER */
+ CCS_TASK_EXECUTE_HANDLER, /* CCS_TASK_IS_EXECUTE_HANDLER */
+ CCS_EXEC_REALPATH,
+ CCS_SYMLINK_TARGET,
+ CCS_PATH1_UID,
+ CCS_PATH1_GID,
+ CCS_PATH1_INO,
+ CCS_PATH1_MAJOR,
+ CCS_PATH1_MINOR,
+ CCS_PATH1_PERM,
+ CCS_PATH1_TYPE,
+ CCS_PATH1_DEV_MAJOR,
+ CCS_PATH1_DEV_MINOR,
+ CCS_PATH2_UID,
+ CCS_PATH2_GID,
+ CCS_PATH2_INO,
+ CCS_PATH2_MAJOR,
+ CCS_PATH2_MINOR,
+ CCS_PATH2_PERM,
+ CCS_PATH2_TYPE,
+ CCS_PATH2_DEV_MAJOR,
+ CCS_PATH2_DEV_MINOR,
+ CCS_PATH1_PARENT_UID,
+ CCS_PATH1_PARENT_GID,
+ CCS_PATH1_PARENT_INO,
+ CCS_PATH1_PARENT_PERM,
+ CCS_PATH2_PARENT_UID,
+ CCS_PATH2_PARENT_GID,
+ CCS_PATH2_PARENT_INO,
+ CCS_PATH2_PARENT_PERM,
+ CCS_MAX_CONDITION_KEYWORD,
+ CCS_NUMBER_UNION,
+ CCS_NAME_UNION,
+ CCS_ARGV_ENTRY,
+ CCS_ENVP_ENTRY,
+};
+
+/* Index numbers for domain's attributes. */
+enum ccs_domain_info_flags_index {
+ /* Quota warnning flag. */
+ CCS_DIF_QUOTA_WARNED,
+ /*
+ * This domain was unable to create a new domain at
+ * ccs_find_next_domain() because the name of the domain to be created
+ * was too long or it could not allocate memory.
+ * More than one process continued execve() without domain transition.
+ */
+ CCS_DIF_TRANSITION_FAILED,
+ CCS_MAX_DOMAIN_INFO_FLAGS
+};
+
+/* Index numbers for audit type. */
+enum ccs_grant_log {
+ /* Follow profile's configuration. */
+ CCS_GRANTLOG_AUTO,
+ /* Do not generate grant log. */
+ CCS_GRANTLOG_NO,
+ /* Generate grant_log. */
+ CCS_GRANTLOG_YES,
+};
+
+/* Index numbers for group entries. */
+enum ccs_group_id {
+ CCS_PATH_GROUP,
+ CCS_NUMBER_GROUP,
+ CCS_ADDRESS_GROUP,
+ CCS_MAX_GROUP
+};
+
+/* Index numbers for category of functionality. */
+enum ccs_mac_category_index {
+ CCS_MAC_CATEGORY_FILE,
+ CCS_MAC_CATEGORY_NETWORK,
+ CCS_MAC_CATEGORY_MISC,
+ CCS_MAC_CATEGORY_IPC,
+ CCS_MAC_CATEGORY_CAPABILITY,
+ CCS_MAX_MAC_CATEGORY_INDEX
+};
+
+/* Index numbers for functionality. */
+enum ccs_mac_index {
+ CCS_MAC_FILE_EXECUTE,
+ CCS_MAC_FILE_OPEN,
+ CCS_MAC_FILE_CREATE,
+ CCS_MAC_FILE_UNLINK,
+ CCS_MAC_FILE_GETATTR,
+ CCS_MAC_FILE_MKDIR,
+ CCS_MAC_FILE_RMDIR,
+ CCS_MAC_FILE_MKFIFO,
+ CCS_MAC_FILE_MKSOCK,
+ CCS_MAC_FILE_TRUNCATE,
+ CCS_MAC_FILE_SYMLINK,
+ CCS_MAC_FILE_MKBLOCK,
+ CCS_MAC_FILE_MKCHAR,
+ CCS_MAC_FILE_LINK,
+ CCS_MAC_FILE_RENAME,
+ CCS_MAC_FILE_CHMOD,
+ CCS_MAC_FILE_CHOWN,
+ CCS_MAC_FILE_CHGRP,
+ CCS_MAC_FILE_IOCTL,
+ CCS_MAC_FILE_CHROOT,
+ CCS_MAC_FILE_MOUNT,
+ CCS_MAC_FILE_UMOUNT,
+ CCS_MAC_FILE_PIVOT_ROOT,
+ CCS_MAC_NETWORK_INET_STREAM_BIND,
+ CCS_MAC_NETWORK_INET_STREAM_LISTEN,
+ CCS_MAC_NETWORK_INET_STREAM_CONNECT,
+ CCS_MAC_NETWORK_INET_STREAM_ACCEPT,
+ CCS_MAC_NETWORK_INET_DGRAM_BIND,
+ CCS_MAC_NETWORK_INET_DGRAM_SEND,
+ CCS_MAC_NETWORK_INET_DGRAM_RECV,
+ CCS_MAC_NETWORK_INET_RAW_BIND,
+ CCS_MAC_NETWORK_INET_RAW_SEND,
+ CCS_MAC_NETWORK_INET_RAW_RECV,
+ CCS_MAC_NETWORK_UNIX_STREAM_BIND,
+ CCS_MAC_NETWORK_UNIX_STREAM_LISTEN,
+ CCS_MAC_NETWORK_UNIX_STREAM_CONNECT,
+ CCS_MAC_NETWORK_UNIX_STREAM_ACCEPT,
+ CCS_MAC_NETWORK_UNIX_DGRAM_BIND,
+ CCS_MAC_NETWORK_UNIX_DGRAM_SEND,
+ CCS_MAC_NETWORK_UNIX_DGRAM_RECV,
+ CCS_MAC_NETWORK_UNIX_SEQPACKET_BIND,
+ CCS_MAC_NETWORK_UNIX_SEQPACKET_LISTEN,
+ CCS_MAC_NETWORK_UNIX_SEQPACKET_CONNECT,
+ CCS_MAC_NETWORK_UNIX_SEQPACKET_ACCEPT,
+ CCS_MAC_ENVIRON,
+ CCS_MAC_SIGNAL,
+ CCS_MAC_CAPABILITY_USE_ROUTE_SOCKET,
+ CCS_MAC_CAPABILITY_USE_PACKET_SOCKET,
+ CCS_MAC_CAPABILITY_SYS_REBOOT,
+ CCS_MAC_CAPABILITY_SYS_VHANGUP,
+ CCS_MAC_CAPABILITY_SYS_SETTIME,
+ CCS_MAC_CAPABILITY_SYS_NICE,
+ CCS_MAC_CAPABILITY_SYS_SETHOSTNAME,
+ CCS_MAC_CAPABILITY_USE_KERNEL_MODULE,
+ CCS_MAC_CAPABILITY_SYS_KEXEC_LOAD,
+ CCS_MAC_CAPABILITY_SYS_PTRACE,
+ CCS_MAX_MAC_INDEX
+};
+
+/* Index numbers for /proc/ccs/stat interface. */
+enum ccs_memory_stat_type {
+ CCS_MEMORY_POLICY,
+ CCS_MEMORY_AUDIT,
+ CCS_MEMORY_QUERY,
+ CCS_MAX_MEMORY_STAT
+};
+
+/* Index numbers for access controls with one pathname and three numbers. */
+enum ccs_mkdev_acl_index {
+ CCS_TYPE_MKBLOCK,
+ CCS_TYPE_MKCHAR,
+ CCS_MAX_MKDEV_OPERATION
+};
+
+/* Index numbers for operation mode. */
+enum ccs_mode_value {
+ CCS_CONFIG_DISABLED,
+ CCS_CONFIG_LEARNING,
+ CCS_CONFIG_PERMISSIVE,
+ CCS_CONFIG_ENFORCING,
+ CCS_CONFIG_MAX_MODE,
+ CCS_CONFIG_WANT_REJECT_LOG = 64,
+ CCS_CONFIG_WANT_GRANT_LOG = 128,
+ CCS_CONFIG_USE_DEFAULT = 255,
+};
+
+/* Index numbers for socket operations. */
+enum ccs_network_acl_index {
+ CCS_NETWORK_BIND, /* bind() operation. */
+ CCS_NETWORK_LISTEN, /* listen() operation. */
+ CCS_NETWORK_CONNECT, /* connect() operation. */
+ CCS_NETWORK_ACCEPT, /* accept() operation. */
+ CCS_NETWORK_SEND, /* send() operation. */
+ CCS_NETWORK_RECV, /* recv() operation. */
+ CCS_MAX_NETWORK_OPERATION
+};
+
+/* Index numbers for access controls with two pathnames. */
+enum ccs_path2_acl_index {
+ CCS_TYPE_LINK,
+ CCS_TYPE_RENAME,
+ CCS_TYPE_PIVOT_ROOT,
+ CCS_MAX_PATH2_OPERATION
+};
+
+/* Index numbers for access controls with one pathname. */
+enum ccs_path_acl_index {
+ CCS_TYPE_EXECUTE,
+ CCS_TYPE_READ,
+ CCS_TYPE_WRITE,
+ CCS_TYPE_APPEND,
+ CCS_TYPE_UNLINK,
+ CCS_TYPE_GETATTR,
+ CCS_TYPE_RMDIR,
+ CCS_TYPE_TRUNCATE,
+ CCS_TYPE_SYMLINK,
+ CCS_TYPE_CHROOT,
+ CCS_TYPE_UMOUNT,
+ CCS_MAX_PATH_OPERATION
+};
+
+/* Index numbers for access controls with one pathname and one number. */
+enum ccs_path_number_acl_index {
+ CCS_TYPE_CREATE,
+ CCS_TYPE_MKDIR,
+ CCS_TYPE_MKFIFO,
+ CCS_TYPE_MKSOCK,
+ CCS_TYPE_IOCTL,
+ CCS_TYPE_CHMOD,
+ CCS_TYPE_CHOWN,
+ CCS_TYPE_CHGRP,
+ CCS_MAX_PATH_NUMBER_OPERATION
+};
+
+/* Index numbers for stat(). */
+enum ccs_path_stat_index {
+ /* Do not change this order. */
+ CCS_PATH1,
+ CCS_PATH1_PARENT,
+ CCS_PATH2,
+ CCS_PATH2_PARENT,
+ CCS_MAX_PATH_STAT
+};
+
+/* Index numbers for /proc/ccs/stat interface. */
+enum ccs_policy_stat_type {
+ /* Do not change this order. */
+ CCS_STAT_POLICY_UPDATES,
+ CCS_STAT_POLICY_LEARNING, /* == CCS_CONFIG_LEARNING */
+ CCS_STAT_POLICY_PERMISSIVE, /* == CCS_CONFIG_PERMISSIVE */
+ CCS_STAT_POLICY_ENFORCING, /* == CCS_CONFIG_ENFORCING */
+ CCS_MAX_POLICY_STAT
+};
+
+/* Index numbers for profile's PREFERENCE values. */
+enum ccs_pref_index {
+ CCS_PREF_MAX_AUDIT_LOG,
+ CCS_PREF_MAX_LEARNING_ENTRY,
+ CCS_PREF_ENFORCING_PENALTY,
+ CCS_MAX_PREF
+};
+
+/* Index numbers for /proc/ccs/ interfaces. */
+enum ccs_proc_interface_index {
+ CCS_DOMAINPOLICY,
+ CCS_EXCEPTIONPOLICY,
+ CCS_PROCESS_STATUS,
+ CCS_STAT,
+ CCS_AUDIT,
+ CCS_VERSION,
+ CCS_PROFILE,
+ CCS_QUERY,
+ CCS_MANAGER,
+ CCS_EXECUTE_HANDLER,
+};
+
+/* Index numbers for special mount operations. */
+enum ccs_special_mount {
+ CCS_MOUNT_BIND, /* mount --bind /source /dest */
+ CCS_MOUNT_MOVE, /* mount --move /old /new */
+ CCS_MOUNT_REMOUNT, /* mount -o remount /dir */
+ CCS_MOUNT_MAKE_UNBINDABLE, /* mount --make-unbindable /dir */
+ CCS_MOUNT_MAKE_PRIVATE, /* mount --make-private /dir */
+ CCS_MOUNT_MAKE_SLAVE, /* mount --make-slave /dir */
+ CCS_MOUNT_MAKE_SHARED, /* mount --make-shared /dir */
+ CCS_MAX_SPECIAL_MOUNT
+};
+
+/* Index numbers for type of numeric values. */
+enum ccs_value_type {
+ CCS_VALUE_TYPE_INVALID,
+ CCS_VALUE_TYPE_DECIMAL,
+ CCS_VALUE_TYPE_OCTAL,
+ CCS_VALUE_TYPE_HEXADECIMAL,
+};
+
+/* Constants definition for internal use. */
+
+/*
+ * TOMOYO uses this hash only when appending a string into the string table.
+ * Frequency of appending strings is very low. So we don't need large (e.g.
+ * 64k) hash size. 256 will be sufficient.
+ */
+#define CCS_HASH_BITS 8
+#define CCS_MAX_HASH (1u << CCS_HASH_BITS)
+
+/*
+ * TOMOYO checks only SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET.
+ * Therefore, we don't need SOCK_MAX.
+ */
+#define CCS_SOCK_MAX 6
+
+/* Size of temporary buffer for execve() operation. */
+#define CCS_EXEC_TMPSIZE 4096
+
+/* Profile number is an integer between 0 and 255. */
+#define CCS_MAX_PROFILES 256
+
+/* Group number is an integer between 0 and 255. */
+#define CCS_MAX_ACL_GROUPS 256
+
+/* Structure definition for internal use. */
+
+struct ccs_policy_namespace;
+
+/* Common header for holding ACL entries. */
+struct ccs_acl_head {
+ struct list_head list;
+ bool is_deleted;
+} __attribute__((__packed__));
+
+/* Common header for shared entries. */
+struct ccs_shared_acl_head {
+ struct list_head list;
+ unsigned int users;
+} __attribute__((__packed__));
+
+/* Common header for individual entries. */
+struct ccs_acl_info {
+ struct list_head list;
+ struct ccs_condition *cond; /* Maybe NULL. */
+ bool is_deleted;
+ u8 type; /* One of values in "enum ccs_acl_entry_type_index". */
+} __attribute__((__packed__));
+
+/* Structure for holding a word. */
+struct ccs_name_union {
+ /* Either @filename or @group is NULL. */
+ const struct ccs_path_info *filename;
+ struct ccs_group *group;
+};
+
+/* Structure for holding a number. */
+struct ccs_number_union {
+ unsigned long values[2];
+ struct ccs_group *group; /* Maybe NULL. */
+ /* One of values in "enum ccs_value_type". */
+ u8 value_type[2];
+};
+
+/* Structure for holding an IP address. */
+struct ccs_ipaddr_union {
+ struct in6_addr ip[2]; /* Big endian. */
+ struct ccs_group *group; /* Pointer to address group. */
+ bool is_ipv6; /* Valid only if @group == NULL. */
+};
+
+/* Structure for "path_group"/"number_group"/"address_group" directive. */
+struct ccs_group {
+ struct ccs_shared_acl_head head;
+ struct ccs_policy_namespace *ns;
+ /* Name of group (without leading '@'). */
+ const struct ccs_path_info *group_name;
+ /*
+ * List of "struct ccs_path_group" or "struct ccs_number_group" or
+ * "struct ccs_address_group".
+ */
+ struct list_head member_list;
+};
+
+/* Structure for "path_group" directive. */
+struct ccs_path_group {
+ struct ccs_acl_head head;
+ const struct ccs_path_info *member_name;
+};
+
+/* Structure for "number_group" directive. */
+struct ccs_number_group {
+ struct ccs_acl_head head;
+ struct ccs_number_union number;
+};
+
+/* Structure for "address_group" directive. */
+struct ccs_address_group {
+ struct ccs_acl_head head;
+ /* Structure for holding an IP address. */
+ struct ccs_ipaddr_union address;
+};
+
+/* Structure for entries which follows "struct ccs_condition". */
+struct ccs_condition_element {
+ /*
+ * Left hand operand. A "struct ccs_argv" for CCS_ARGV_ENTRY, a
+ * "struct ccs_envp" for CCS_ENVP_ENTRY is attached to the tail
+ * of the array of this struct.
+ */
+ u8 left;
+ /*
+ * Right hand operand. A "struct ccs_number_union" for
+ * CCS_NUMBER_UNION, a "struct ccs_name_union" for CCS_NAME_UNION is
+ * attached to the tail of the array of this struct.
+ */
+ u8 right;
+ /* Equation operator. True if equals or overlaps, false otherwise. */
+ bool equals;
+};
+
+/* Structure for optional arguments. */
+struct ccs_condition {
+ struct ccs_shared_acl_head head;
+ u32 size; /* Memory size allocated for this entry. */
+ u16 condc; /* Number of conditions in this struct. */
+ u16 numbers_count; /* Number of "struct ccs_number_union values". */
+ u16 names_count; /* Number of "struct ccs_name_union names". */
+ u16 argc; /* Number of "struct ccs_argv". */
+ u16 envc; /* Number of "struct ccs_envp". */
+ u8 grant_log; /* One of values in "enum ccs_grant_log". */
+ const struct ccs_path_info *transit; /* Maybe NULL. */
+ /*
+ * struct ccs_condition_element condition[condc];
+ * struct ccs_number_union values[numbers_count];
+ * struct ccs_name_union names[names_count];
+ * struct ccs_argv argv[argc];
+ * struct ccs_envp envp[envc];
+ */
+};
+
+/*
+ * Structure for "reset_domain"/"no_reset_domain"/"initialize_domain"/
+ * "no_initialize_domain"/"keep_domain"/"no_keep_domain" keyword.
+ */
+struct ccs_transition_control {
+ struct ccs_acl_head head;
+ u8 type; /* One of values in "enum ccs_transition_type" */
+ bool is_last_name; /* True if the domainname is ccs_last_word(). */
+ const struct ccs_path_info *domainname; /* Maybe NULL */
+ const struct ccs_path_info *program; /* Maybe NULL */
+ struct ccs_policy_namespace *ns;
+};
+
+/* Structure for "aggregator" keyword. */
+struct ccs_aggregator {
+ struct ccs_acl_head head;
+ const struct ccs_path_info *original_name;
+ const struct ccs_path_info *aggregated_name;
+ struct ccs_policy_namespace *ns;
+};
+
+/* Structure for "deny_autobind" keyword. */
+struct ccs_reserved {
+ struct ccs_acl_head head;
+ struct ccs_number_union port;
+ struct ccs_policy_namespace *ns;
+};
+
+/* Structure for policy manager. */
+struct ccs_manager {
+ struct ccs_acl_head head;
+ bool is_domain; /* True if manager is a domainname. */
+ /* A path to program or a domainname. */
+ const struct ccs_path_info *manager;
+};
+
+/* Structure for argv[]. */
+struct ccs_argv {
+ unsigned long index;
+ const struct ccs_path_info *value;
+ bool is_not;
+};
+
+/* Structure for envp[]. */
+struct ccs_envp {
+ const struct ccs_path_info *name;
+ const struct ccs_path_info *value;
+ bool is_not;
+};
+
+/*
+ * Structure for "task auto_execute_handler" and "task denied_execute_handler"
+ * directive.
+ *
+ * If "task auto_execute_handler" directive exists and the current process is
+ * not an execute handler, all execve() requests are replaced by execve()
+ * requests of a program specified by "task auto_execute_handler" directive.
+ * If the current process is an execute handler, "task auto_execute_handler"
+ * and "task denied_execute_handler" directives are ignored.
+ * The program specified by "task execute_handler" validates execve()
+ * parameters and executes the original execve() requests if appropriate.
+ *
+ * "task denied_execute_handler" directive is used only when execve() request
+ * was rejected in enforcing mode (i.e. CONFIG::file::execute={ mode=enforcing
+ * }). The program specified by "task denied_execute_handler" does whatever it
+ * wants to do (e.g. silently terminate, change firewall settings, redirect the
+ * user to honey pot etc.).
+ */
+struct ccs_handler_acl {
+ struct ccs_acl_info head; /* type = CCS_TYPE_*_EXECUTE_HANDLER */
+ const struct ccs_path_info *handler; /* Pointer to single pathname. */
+};
+
+/*
+ * Structure for "task auto_domain_transition" and
+ * "task manual_domain_transition" directive.
+ */
+struct ccs_task_acl {
+ struct ccs_acl_info head; /* type = CCS_TYPE_*_TASK_ACL */
+ /* Pointer to domainname. */
+ const struct ccs_path_info *domainname;
+};
+
+/*
+ * Structure for "file execute", "file read", "file write", "file append",
+ * "file unlink", "file getattr", "file rmdir", "file truncate",
+ * "file symlink", "file chroot" and "file unmount" directive.
+ */
+struct ccs_path_acl {
+ struct ccs_acl_info head; /* type = CCS_TYPE_PATH_ACL */
+ u16 perm; /* Bitmask of values in "enum ccs_path_acl_index". */
+ struct ccs_name_union name;
+};
+
+/*
+ * Structure for "file rename", "file link" and "file pivot_root" directive.
+ */
+struct ccs_path2_acl {
+ struct ccs_acl_info head; /* type = CCS_TYPE_PATH2_ACL */
+ u8 perm; /* Bitmask of values in "enum ccs_path2_acl_index". */
+ struct ccs_name_union name1;
+ struct ccs_name_union name2;
+};
+
+/*
+ * Structure for "file create", "file mkdir", "file mkfifo", "file mksock",
+ * "file ioctl", "file chmod", "file chown" and "file chgrp" directive.
+ */
+struct ccs_path_number_acl {
+ struct ccs_acl_info head; /* type = CCS_TYPE_PATH_NUMBER_ACL */
+ u8 perm; /* Bitmask of values in "enum ccs_path_number_acl_index". */
+ struct ccs_name_union name;
+ struct ccs_number_union number;
+};
+
+/* Structure for "file mkblock" and "file mkchar" directive. */
+struct ccs_mkdev_acl {
+ struct ccs_acl_info head; /* type = CCS_TYPE_MKDEV_ACL */
+ u8 perm; /* Bitmask of values in "enum ccs_mkdev_acl_index". */
+ struct ccs_name_union name;
+ struct ccs_number_union mode;
+ struct ccs_number_union major;
+ struct ccs_number_union minor;
+};
+
+/* Structure for "file mount" directive. */
+struct ccs_mount_acl {
+ struct ccs_acl_info head; /* type = CCS_TYPE_MOUNT_ACL */
+ struct ccs_name_union dev_name;
+ struct ccs_name_union dir_name;
+ struct ccs_name_union fs_type;
+ struct ccs_number_union flags;
+};
+
+/* Structure for "misc env" directive in domain policy. */
+struct ccs_env_acl {
+ struct ccs_acl_info head; /* type = CCS_TYPE_ENV_ACL */
+ const struct ccs_path_info *env; /* environment variable */
+};
+
+/* Structure for "capability" directive. */
+struct ccs_capability_acl {
+ struct ccs_acl_info head; /* type = CCS_TYPE_CAPABILITY_ACL */
+ u8 operation; /* One of values in "enum ccs_capability_acl_index". */
+};
+
+/* Structure for "ipc signal" directive. */
+struct ccs_signal_acl {
+ struct ccs_acl_info head; /* type = CCS_TYPE_SIGNAL_ACL */
+ struct ccs_number_union sig;
+ /* Pointer to destination pattern. */
+ const struct ccs_path_info *domainname;
+};
+
+/* Structure for "network inet" directive. */
+struct ccs_inet_acl {
+ struct ccs_acl_info head; /* type = CCS_TYPE_INET_ACL */
+ u8 protocol;
+ u8 perm; /* Bitmask of values in "enum ccs_network_acl_index" */
+ struct ccs_ipaddr_union address;
+ struct ccs_number_union port;
+};
+
+/* Structure for "network unix" directive. */
+struct ccs_unix_acl {
+ struct ccs_acl_info head; /* type = CCS_TYPE_UNIX_ACL */
+ u8 protocol;
+ u8 perm; /* Bitmask of values in "enum ccs_network_acl_index" */
+ struct ccs_name_union name;
+};
+
+/* Structure for holding string data. */
+struct ccs_name {
+ struct ccs_shared_acl_head head;
+ int size; /* Memory size allocated for this entry. */
+ struct ccs_path_info entry;
+};
+
+/* Structure for holding a line from /proc/ccs/ interface. */
+struct ccs_acl_param {
+ char *namespace;
+ char *data; /* Unprocessed data. */
+ struct list_head *list; /* List to add or remove. */
+ struct ccs_policy_namespace *ns; /* Namespace to use. */
+ bool is_delete; /* True if it is a delete request. */
+};
+
+/* Structure for /proc/ccs/profile interface. */
+struct ccs_profile {
+ const struct ccs_path_info *comment;
+ u8 default_config;
+ u8 config[CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX];
+ unsigned int pref[CCS_MAX_PREF];
+};
+
+/* Structure for representing YYYY/MM/DD hh/mm/ss. */
+struct ccs_time {
+ u16 year;
+ u8 month;
+ u8 day;
+ u8 hour;
+ u8 min;
+ u8 sec;
+};
+
+/* Structure for policy namespace. */
+struct ccs_policy_namespace {
+ /* Profile table. Memory is allocated as needed. */
+ struct ccs_profile *profile_ptr[CCS_MAX_PROFILES];
+ /* The global ACL referred by "use_group" keyword. */
+ struct list_head acl_group[CCS_MAX_ACL_GROUPS];
+ /* List for connecting to ccs_namespace_list list. */
+ struct list_head namespace_list;
+ /* Profile version. Currently only 20100903 is defined. */
+ unsigned int profile_version;
+ /* Name of this namespace (e.g. "<kernel>", "</usr/sbin/httpd>" ). */
+ const char *name;
+};
+
+struct ccs_domain2_info {
+ struct list_head list;
+ struct list_head acl_info_list;
+ /* Name of this domain. Never NULL. */
+ const struct ccs_path_info *domainname;
+ u8 profile; /* Profile number to use. */
+ u8 group; /* Group number to use. */
+ bool is_deleted; /* Delete flag. */
+ bool flags[CCS_MAX_DOMAIN_INFO_FLAGS];
+};
+
+struct ccs_io_buffer {
+ char *data;
+ struct ccs_policy_namespace *ns;
+ struct ccs_domain2_info *domain;
+ struct ccs_domain2_info *print_this_domain_only;
+ bool is_delete;
+ bool print_transition_related_only;
+ bool eof;
+ bool reset;
+ u8 type;
+ u8 acl_group_index;
+};
+
+/* String table for operation mode. */
+static const char * const ccs_mode[CCS_CONFIG_MAX_MODE] = {
+ [CCS_CONFIG_DISABLED] = "disabled",
+ [CCS_CONFIG_LEARNING] = "learning",
+ [CCS_CONFIG_PERMISSIVE] = "permissive",
+ [CCS_CONFIG_ENFORCING] = "enforcing"
+};
+
+/* String table for /proc/ccs/profile interface. */
+static const char * const ccs_mac_keywords[CCS_MAX_MAC_INDEX
+ + CCS_MAX_MAC_CATEGORY_INDEX] = {
+ /* CONFIG::file group */
+ [CCS_MAC_FILE_EXECUTE] = "execute",
+ [CCS_MAC_FILE_OPEN] = "open",
+ [CCS_MAC_FILE_CREATE] = "create",
+ [CCS_MAC_FILE_UNLINK] = "unlink",
+ [CCS_MAC_FILE_GETATTR] = "getattr",
+ [CCS_MAC_FILE_MKDIR] = "mkdir",
+ [CCS_MAC_FILE_RMDIR] = "rmdir",
+ [CCS_MAC_FILE_MKFIFO] = "mkfifo",
+ [CCS_MAC_FILE_MKSOCK] = "mksock",
+ [CCS_MAC_FILE_TRUNCATE] = "truncate",
+ [CCS_MAC_FILE_SYMLINK] = "symlink",
+ [CCS_MAC_FILE_MKBLOCK] = "mkblock",
+ [CCS_MAC_FILE_MKCHAR] = "mkchar",
+ [CCS_MAC_FILE_LINK] = "link",
+ [CCS_MAC_FILE_RENAME] = "rename",
+ [CCS_MAC_FILE_CHMOD] = "chmod",
+ [CCS_MAC_FILE_CHOWN] = "chown",
+ [CCS_MAC_FILE_CHGRP] = "chgrp",
+ [CCS_MAC_FILE_IOCTL] = "ioctl",
+ [CCS_MAC_FILE_CHROOT] = "chroot",
+ [CCS_MAC_FILE_MOUNT] = "mount",
+ [CCS_MAC_FILE_UMOUNT] = "unmount",
+ [CCS_MAC_FILE_PIVOT_ROOT] = "pivot_root",
+ /* CONFIG::misc group */
+ [CCS_MAC_ENVIRON] = "env",
+ /* CONFIG::network group */
+ [CCS_MAC_NETWORK_INET_STREAM_BIND] = "inet_stream_bind",
+ [CCS_MAC_NETWORK_INET_STREAM_LISTEN] = "inet_stream_listen",
+ [CCS_MAC_NETWORK_INET_STREAM_CONNECT] = "inet_stream_connect",
+ [CCS_MAC_NETWORK_INET_STREAM_ACCEPT] = "inet_stream_accept",
+ [CCS_MAC_NETWORK_INET_DGRAM_BIND] = "inet_dgram_bind",
+ [CCS_MAC_NETWORK_INET_DGRAM_SEND] = "inet_dgram_send",
+ [CCS_MAC_NETWORK_INET_DGRAM_RECV] = "inet_dgram_recv",
+ [CCS_MAC_NETWORK_INET_RAW_BIND] = "inet_raw_bind",
+ [CCS_MAC_NETWORK_INET_RAW_SEND] = "inet_raw_send",
+ [CCS_MAC_NETWORK_INET_RAW_RECV] = "inet_raw_recv",
+ [CCS_MAC_NETWORK_UNIX_STREAM_BIND] = "unix_stream_bind",
+ [CCS_MAC_NETWORK_UNIX_STREAM_LISTEN] = "unix_stream_listen",
+ [CCS_MAC_NETWORK_UNIX_STREAM_CONNECT] = "unix_stream_connect",
+ [CCS_MAC_NETWORK_UNIX_STREAM_ACCEPT] = "unix_stream_accept",
+ [CCS_MAC_NETWORK_UNIX_DGRAM_BIND] = "unix_dgram_bind",
+ [CCS_MAC_NETWORK_UNIX_DGRAM_SEND] = "unix_dgram_send",
+ [CCS_MAC_NETWORK_UNIX_DGRAM_RECV] = "unix_dgram_recv",
+ [CCS_MAC_NETWORK_UNIX_SEQPACKET_BIND] = "unix_seqpacket_bind",
+ [CCS_MAC_NETWORK_UNIX_SEQPACKET_LISTEN] = "unix_seqpacket_listen",
+ [CCS_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = "unix_seqpacket_connect",
+ [CCS_MAC_NETWORK_UNIX_SEQPACKET_ACCEPT] = "unix_seqpacket_accept",
+ /* CONFIG::ipc group */
+ [CCS_MAC_SIGNAL] = "signal",
+ /* CONFIG::capability group */
+ [CCS_MAC_CAPABILITY_USE_ROUTE_SOCKET] = "use_route",
+ [CCS_MAC_CAPABILITY_USE_PACKET_SOCKET] = "use_packet",
+ [CCS_MAC_CAPABILITY_SYS_REBOOT] = "SYS_REBOOT",
+ [CCS_MAC_CAPABILITY_SYS_VHANGUP] = "SYS_VHANGUP",
+ [CCS_MAC_CAPABILITY_SYS_SETTIME] = "SYS_TIME",
+ [CCS_MAC_CAPABILITY_SYS_NICE] = "SYS_NICE",
+ [CCS_MAC_CAPABILITY_SYS_SETHOSTNAME] = "SYS_SETHOSTNAME",
+ [CCS_MAC_CAPABILITY_USE_KERNEL_MODULE] = "use_kernel_module",
+ [CCS_MAC_CAPABILITY_SYS_KEXEC_LOAD] = "SYS_KEXEC_LOAD",
+ [CCS_MAC_CAPABILITY_SYS_PTRACE] = "SYS_PTRACE",
+ /* CONFIG group */
+ [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_FILE] = "file",
+ [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_NETWORK] = "network",
+ [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_MISC] = "misc",
+ [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_IPC] = "ipc",
+ [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_CAPABILITY] = "capability",
+};
+
+/* String table for path operation. */
+static const char * const ccs_path_keyword[CCS_MAX_PATH_OPERATION] = {
+ [CCS_TYPE_EXECUTE] = "execute",
+ [CCS_TYPE_READ] = "read",
+ [CCS_TYPE_WRITE] = "write",
+ [CCS_TYPE_APPEND] = "append",
+ [CCS_TYPE_UNLINK] = "unlink",
+ [CCS_TYPE_GETATTR] = "getattr",
+ [CCS_TYPE_RMDIR] = "rmdir",
+ [CCS_TYPE_TRUNCATE] = "truncate",
+ [CCS_TYPE_SYMLINK] = "symlink",
+ [CCS_TYPE_CHROOT] = "chroot",
+ [CCS_TYPE_UMOUNT] = "unmount",
+};
+
+/* String table for socket's operation. */
+static const char * const ccs_socket_keyword[CCS_MAX_NETWORK_OPERATION] = {
+ [CCS_NETWORK_BIND] = "bind",
+ [CCS_NETWORK_LISTEN] = "listen",
+ [CCS_NETWORK_CONNECT] = "connect",
+ [CCS_NETWORK_ACCEPT] = "accept",
+ [CCS_NETWORK_SEND] = "send",
+ [CCS_NETWORK_RECV] = "recv",
+};
+
+/* String table for categories. */
+static const char * const ccs_category_keywords[CCS_MAX_MAC_CATEGORY_INDEX] = {
+ [CCS_MAC_CATEGORY_FILE] = "file",
+ [CCS_MAC_CATEGORY_NETWORK] = "network",
+ [CCS_MAC_CATEGORY_MISC] = "misc",
+ [CCS_MAC_CATEGORY_IPC] = "ipc",
+ [CCS_MAC_CATEGORY_CAPABILITY] = "capability",
+};
+
+/* String table for conditions. */
+static const char * const ccs_condition_keyword[CCS_MAX_CONDITION_KEYWORD] = {
+ [CCS_TASK_UID] = "task.uid",
+ [CCS_TASK_EUID] = "task.euid",
+ [CCS_TASK_SUID] = "task.suid",
+ [CCS_TASK_FSUID] = "task.fsuid",
+ [CCS_TASK_GID] = "task.gid",
+ [CCS_TASK_EGID] = "task.egid",
+ [CCS_TASK_SGID] = "task.sgid",
+ [CCS_TASK_FSGID] = "task.fsgid",
+ [CCS_TASK_PID] = "task.pid",
+ [CCS_TASK_PPID] = "task.ppid",
+ [CCS_EXEC_ARGC] = "exec.argc",
+ [CCS_EXEC_ENVC] = "exec.envc",
+ [CCS_TYPE_IS_SOCKET] = "socket",
+ [CCS_TYPE_IS_SYMLINK] = "symlink",
+ [CCS_TYPE_IS_FILE] = "file",
+ [CCS_TYPE_IS_BLOCK_DEV] = "block",
+ [CCS_TYPE_IS_DIRECTORY] = "directory",
+ [CCS_TYPE_IS_CHAR_DEV] = "char",
+ [CCS_TYPE_IS_FIFO] = "fifo",
+ [CCS_MODE_SETUID] = "setuid",
+ [CCS_MODE_SETGID] = "setgid",
+ [CCS_MODE_STICKY] = "sticky",
+ [CCS_MODE_OWNER_READ] = "owner_read",
+ [CCS_MODE_OWNER_WRITE] = "owner_write",
+ [CCS_MODE_OWNER_EXECUTE] = "owner_execute",
+ [CCS_MODE_GROUP_READ] = "group_read",
+ [CCS_MODE_GROUP_WRITE] = "group_write",
+ [CCS_MODE_GROUP_EXECUTE] = "group_execute",
+ [CCS_MODE_OTHERS_READ] = "others_read",
+ [CCS_MODE_OTHERS_WRITE] = "others_write",
+ [CCS_MODE_OTHERS_EXECUTE] = "others_execute",
+ [CCS_TASK_TYPE] = "task.type",
+ [CCS_TASK_EXECUTE_HANDLER] = "execute_handler",
+ [CCS_EXEC_REALPATH] = "exec.realpath",
+ [CCS_SYMLINK_TARGET] = "symlink.target",
+ [CCS_PATH1_UID] = "path1.uid",
+ [CCS_PATH1_GID] = "path1.gid",
+ [CCS_PATH1_INO] = "path1.ino",
+ [CCS_PATH1_MAJOR] = "path1.major",
+ [CCS_PATH1_MINOR] = "path1.minor",
+ [CCS_PATH1_PERM] = "path1.perm",
+ [CCS_PATH1_TYPE] = "path1.type",
+ [CCS_PATH1_DEV_MAJOR] = "path1.dev_major",
+ [CCS_PATH1_DEV_MINOR] = "path1.dev_minor",
+ [CCS_PATH2_UID] = "path2.uid",
+ [CCS_PATH2_GID] = "path2.gid",
+ [CCS_PATH2_INO] = "path2.ino",
+ [CCS_PATH2_MAJOR] = "path2.major",
+ [CCS_PATH2_MINOR] = "path2.minor",
+ [CCS_PATH2_PERM] = "path2.perm",
+ [CCS_PATH2_TYPE] = "path2.type",
+ [CCS_PATH2_DEV_MAJOR] = "path2.dev_major",
+ [CCS_PATH2_DEV_MINOR] = "path2.dev_minor",
+ [CCS_PATH1_PARENT_UID] = "path1.parent.uid",
+ [CCS_PATH1_PARENT_GID] = "path1.parent.gid",
+ [CCS_PATH1_PARENT_INO] = "path1.parent.ino",
+ [CCS_PATH1_PARENT_PERM] = "path1.parent.perm",
+ [CCS_PATH2_PARENT_UID] = "path2.parent.uid",
+ [CCS_PATH2_PARENT_GID] = "path2.parent.gid",
+ [CCS_PATH2_PARENT_INO] = "path2.parent.ino",
+ [CCS_PATH2_PARENT_PERM] = "path2.parent.perm",
+};
+
+/* String table for PREFERENCE keyword. */
+static const char * const ccs_pref_keywords[CCS_MAX_PREF] = {
+ [CCS_PREF_MAX_AUDIT_LOG] = "max_audit_log",
+ [CCS_PREF_MAX_LEARNING_ENTRY] = "max_learning_entry",
+ [CCS_PREF_ENFORCING_PENALTY] = "enforcing_penalty",
+};
+
+/* Mapping table from "enum ccs_path_acl_index" to "enum ccs_mac_index". */
+static const u8 ccs_p2mac[CCS_MAX_PATH_OPERATION] = {
+ [CCS_TYPE_EXECUTE] = CCS_MAC_FILE_EXECUTE,
+ [CCS_TYPE_READ] = CCS_MAC_FILE_OPEN,
+ [CCS_TYPE_WRITE] = CCS_MAC_FILE_OPEN,
+ [CCS_TYPE_APPEND] = CCS_MAC_FILE_OPEN,
+ [CCS_TYPE_UNLINK] = CCS_MAC_FILE_UNLINK,
+ [CCS_TYPE_GETATTR] = CCS_MAC_FILE_GETATTR,
+ [CCS_TYPE_RMDIR] = CCS_MAC_FILE_RMDIR,
+ [CCS_TYPE_TRUNCATE] = CCS_MAC_FILE_TRUNCATE,
+ [CCS_TYPE_SYMLINK] = CCS_MAC_FILE_SYMLINK,
+ [CCS_TYPE_CHROOT] = CCS_MAC_FILE_CHROOT,
+ [CCS_TYPE_UMOUNT] = CCS_MAC_FILE_UMOUNT,
+};
+
+/* Mapping table from "enum ccs_mkdev_acl_index" to "enum ccs_mac_index". */
+static const u8 ccs_pnnn2mac[CCS_MAX_MKDEV_OPERATION] = {
+ [CCS_TYPE_MKBLOCK] = CCS_MAC_FILE_MKBLOCK,
+ [CCS_TYPE_MKCHAR] = CCS_MAC_FILE_MKCHAR,
+};
+
+/* Mapping table from "enum ccs_path2_acl_index" to "enum ccs_mac_index". */
+static const u8 ccs_pp2mac[CCS_MAX_PATH2_OPERATION] = {
+ [CCS_TYPE_LINK] = CCS_MAC_FILE_LINK,
+ [CCS_TYPE_RENAME] = CCS_MAC_FILE_RENAME,
+ [CCS_TYPE_PIVOT_ROOT] = CCS_MAC_FILE_PIVOT_ROOT,
+};
+
+/*
+ * Mapping table from "enum ccs_path_number_acl_index" to "enum ccs_mac_index".
+ */
+static const u8 ccs_pn2mac[CCS_MAX_PATH_NUMBER_OPERATION] = {
+ [CCS_TYPE_CREATE] = CCS_MAC_FILE_CREATE,
+ [CCS_TYPE_MKDIR] = CCS_MAC_FILE_MKDIR,
+ [CCS_TYPE_MKFIFO] = CCS_MAC_FILE_MKFIFO,
+ [CCS_TYPE_MKSOCK] = CCS_MAC_FILE_MKSOCK,
+ [CCS_TYPE_IOCTL] = CCS_MAC_FILE_IOCTL,
+ [CCS_TYPE_CHMOD] = CCS_MAC_FILE_CHMOD,
+ [CCS_TYPE_CHOWN] = CCS_MAC_FILE_CHOWN,
+ [CCS_TYPE_CHGRP] = CCS_MAC_FILE_CHGRP,
+};
+
+/*
+ * Mapping table from "enum ccs_network_acl_index" to "enum ccs_mac_index" for
+ * inet domain socket.
+ */
+static const u8 ccs_inet2mac[CCS_SOCK_MAX][CCS_MAX_NETWORK_OPERATION] = {
+ [SOCK_STREAM] = {
+ [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_INET_STREAM_BIND,
+ [CCS_NETWORK_LISTEN] = CCS_MAC_NETWORK_INET_STREAM_LISTEN,
+ [CCS_NETWORK_CONNECT] = CCS_MAC_NETWORK_INET_STREAM_CONNECT,
+ [CCS_NETWORK_ACCEPT] = CCS_MAC_NETWORK_INET_STREAM_ACCEPT,
+ },
+ [SOCK_DGRAM] = {
+ [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_INET_DGRAM_BIND,
+ [CCS_NETWORK_SEND] = CCS_MAC_NETWORK_INET_DGRAM_SEND,
+ [CCS_NETWORK_RECV] = CCS_MAC_NETWORK_INET_DGRAM_RECV,
+ },
+ [SOCK_RAW] = {
+ [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_INET_RAW_BIND,
+ [CCS_NETWORK_SEND] = CCS_MAC_NETWORK_INET_RAW_SEND,
+ [CCS_NETWORK_RECV] = CCS_MAC_NETWORK_INET_RAW_RECV,
+ },
+};
+
+/*
+ * Mapping table from "enum ccs_network_acl_index" to "enum ccs_mac_index" for
+ * unix domain socket.
+ */
+static const u8 ccs_unix2mac[CCS_SOCK_MAX][CCS_MAX_NETWORK_OPERATION] = {
+ [SOCK_STREAM] = {
+ [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_UNIX_STREAM_BIND,
+ [CCS_NETWORK_LISTEN] = CCS_MAC_NETWORK_UNIX_STREAM_LISTEN,
+ [CCS_NETWORK_CONNECT] = CCS_MAC_NETWORK_UNIX_STREAM_CONNECT,
+ [CCS_NETWORK_ACCEPT] = CCS_MAC_NETWORK_UNIX_STREAM_ACCEPT,
+ },
+ [SOCK_DGRAM] = {
+ [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_UNIX_DGRAM_BIND,
+ [CCS_NETWORK_SEND] = CCS_MAC_NETWORK_UNIX_DGRAM_SEND,
+ [CCS_NETWORK_RECV] = CCS_MAC_NETWORK_UNIX_DGRAM_RECV,
+ },
+ [SOCK_SEQPACKET] = {
+ [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_UNIX_SEQPACKET_BIND,
+ [CCS_NETWORK_LISTEN] = CCS_MAC_NETWORK_UNIX_SEQPACKET_LISTEN,
+ [CCS_NETWORK_CONNECT] = CCS_MAC_NETWORK_UNIX_SEQPACKET_CONNECT,
+ [CCS_NETWORK_ACCEPT] = CCS_MAC_NETWORK_UNIX_SEQPACKET_ACCEPT,
+ },
+};
+
+/* String table for socket's protocols. */
+static const char * const ccs_proto_keyword[CCS_SOCK_MAX] = {
+ [SOCK_STREAM] = "stream",
+ [SOCK_DGRAM] = "dgram",
+ [SOCK_RAW] = "raw",
+ [SOCK_SEQPACKET] = "seqpacket",
+ [0] = " ", /* Dummy for avoiding NULL pointer dereference. */
+ [4] = " ", /* Dummy for avoiding NULL pointer dereference. */
+};
+
+/*
+ * Mapping table from "enum ccs_capability_acl_index" to "enum ccs_mac_index".
+ */
+static const u8 ccs_c2mac[CCS_MAX_CAPABILITY_INDEX] = {
+ [CCS_USE_ROUTE_SOCKET] = CCS_MAC_CAPABILITY_USE_ROUTE_SOCKET,
+ [CCS_USE_PACKET_SOCKET] = CCS_MAC_CAPABILITY_USE_PACKET_SOCKET,
+ [CCS_SYS_REBOOT] = CCS_MAC_CAPABILITY_SYS_REBOOT,
+ [CCS_SYS_VHANGUP] = CCS_MAC_CAPABILITY_SYS_VHANGUP,
+ [CCS_SYS_SETTIME] = CCS_MAC_CAPABILITY_SYS_SETTIME,
+ [CCS_SYS_NICE] = CCS_MAC_CAPABILITY_SYS_NICE,
+ [CCS_SYS_SETHOSTNAME] = CCS_MAC_CAPABILITY_SYS_SETHOSTNAME,
+ [CCS_USE_KERNEL_MODULE] = CCS_MAC_CAPABILITY_USE_KERNEL_MODULE,
+ [CCS_SYS_KEXEC_LOAD] = CCS_MAC_CAPABILITY_SYS_KEXEC_LOAD,
+ [CCS_SYS_PTRACE] = CCS_MAC_CAPABILITY_SYS_PTRACE,
+};
+
+/* String table for /proc/ccs/stat interface. */
+static const char * const ccs_memory_headers[CCS_MAX_MEMORY_STAT] = {
+ [CCS_MEMORY_POLICY] = "policy:",
+ [CCS_MEMORY_AUDIT] = "audit log:",
+ [CCS_MEMORY_QUERY] = "query message:",
+};
+
+/* String table for domain transition control keywords. */
+static const char * const ccs_transition_type[CCS_MAX_TRANSITION_TYPE] = {
+ [CCS_TRANSITION_CONTROL_NO_RESET] = "no_reset_domain ",
+ [CCS_TRANSITION_CONTROL_RESET] = "reset_domain ",
+ [CCS_TRANSITION_CONTROL_NO_INITIALIZE] = "no_initialize_domain ",
+ [CCS_TRANSITION_CONTROL_INITIALIZE] = "initialize_domain ",
+ [CCS_TRANSITION_CONTROL_NO_KEEP] = "no_keep_domain ",
+ [CCS_TRANSITION_CONTROL_KEEP] = "keep_domain ",
+};
+
+/* String table for grouping keywords. */
+static const char * const ccs_group_name[CCS_MAX_GROUP] = {
+ [CCS_PATH_GROUP] = "path_group ",
+ [CCS_NUMBER_GROUP] = "number_group ",
+ [CCS_ADDRESS_GROUP] = "address_group ",
+};
+
+/* String table for domain flags. */
+static const char * const ccs_dif[CCS_MAX_DOMAIN_INFO_FLAGS] = {
+ [CCS_DIF_QUOTA_WARNED] = "quota_exceeded\n",
+ [CCS_DIF_TRANSITION_FAILED] = "transition_failed\n",
+};
+
+/* Mapping table from "enum ccs_mac_index" to "enum ccs_mac_category_index". */
+static const u8 ccs_index2category[CCS_MAX_MAC_INDEX] = {
+ /* CONFIG::file group */
+ [CCS_MAC_FILE_EXECUTE] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_OPEN] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_CREATE] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_UNLINK] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_GETATTR] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_MKDIR] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_RMDIR] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_MKFIFO] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_MKSOCK] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_TRUNCATE] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_SYMLINK] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_MKBLOCK] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_MKCHAR] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_LINK] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_RENAME] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_CHMOD] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_CHOWN] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_CHGRP] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_IOCTL] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_CHROOT] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_MOUNT] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_UMOUNT] = CCS_MAC_CATEGORY_FILE,
+ [CCS_MAC_FILE_PIVOT_ROOT] = CCS_MAC_CATEGORY_FILE,
+ /* CONFIG::misc group */
+ [CCS_MAC_ENVIRON] = CCS_MAC_CATEGORY_MISC,
+ /* CONFIG::network group */
+ [CCS_MAC_NETWORK_INET_STREAM_BIND] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_INET_STREAM_LISTEN] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_INET_STREAM_CONNECT] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_INET_STREAM_ACCEPT] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_INET_DGRAM_BIND] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_INET_DGRAM_SEND] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_INET_DGRAM_RECV] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_INET_RAW_BIND] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_INET_RAW_SEND] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_INET_RAW_RECV] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_UNIX_STREAM_BIND] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_UNIX_STREAM_LISTEN] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_UNIX_STREAM_CONNECT] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_UNIX_STREAM_ACCEPT] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_UNIX_DGRAM_BIND] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_UNIX_DGRAM_SEND] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_UNIX_DGRAM_RECV] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_UNIX_SEQPACKET_BIND] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_UNIX_SEQPACKET_LISTEN] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = CCS_MAC_CATEGORY_NETWORK,
+ [CCS_MAC_NETWORK_UNIX_SEQPACKET_ACCEPT] = CCS_MAC_CATEGORY_NETWORK,
+ /* CONFIG::ipc group */
+ [CCS_MAC_SIGNAL] = CCS_MAC_CATEGORY_IPC,
+ /* CONFIG::capability group */
+ [CCS_MAC_CAPABILITY_USE_ROUTE_SOCKET] = CCS_MAC_CATEGORY_CAPABILITY,
+ [CCS_MAC_CAPABILITY_USE_PACKET_SOCKET] = CCS_MAC_CATEGORY_CAPABILITY,
+ [CCS_MAC_CAPABILITY_SYS_REBOOT] = CCS_MAC_CATEGORY_CAPABILITY,
+ [CCS_MAC_CAPABILITY_SYS_VHANGUP] = CCS_MAC_CATEGORY_CAPABILITY,
+ [CCS_MAC_CAPABILITY_SYS_SETTIME] = CCS_MAC_CATEGORY_CAPABILITY,
+ [CCS_MAC_CAPABILITY_SYS_NICE] = CCS_MAC_CATEGORY_CAPABILITY,
+ [CCS_MAC_CAPABILITY_SYS_SETHOSTNAME] = CCS_MAC_CATEGORY_CAPABILITY,
+ [CCS_MAC_CAPABILITY_USE_KERNEL_MODULE] = CCS_MAC_CATEGORY_CAPABILITY,
+ [CCS_MAC_CAPABILITY_SYS_KEXEC_LOAD] = CCS_MAC_CATEGORY_CAPABILITY,
+ [CCS_MAC_CAPABILITY_SYS_PTRACE] = CCS_MAC_CATEGORY_CAPABILITY,
+};
+
+static struct ccs_io_buffer head;
+static struct ccs_domain2_info ccs_kernel_domain;
+static struct ccs_policy_namespace ccs_kernel_namespace;
+static LIST_HEAD(ccs_domain_list);
+static LIST_HEAD(ccs_manager_list);
+static LIST_HEAD(ccs_path_group);
+static LIST_HEAD(ccs_number_group);
+static LIST_HEAD(ccs_address_group);
+static LIST_HEAD(ccs_transition_list);
+static LIST_HEAD(ccs_aggregator_list);
+static LIST_HEAD(ccs_reserved_list);
+static LIST_HEAD(ccs_namespace_list);
+static bool ccs_namespace_enabled;
+static struct list_head ccs_name_list[CCS_MAX_HASH];
+static unsigned int ccs_memory_quota[CCS_MAX_MEMORY_STAT];
+
+/**
+ * ccs_put_condition - Drop reference on "struct ccs_condition".
+ *
+ * @cond: Pointer to "struct ccs_condition". Maybe NULL.
+ *
+ * Returns nothing.
+ */
+static inline void ccs_put_condition(struct ccs_condition *cond)
+{
+ if (cond)
+ cond->head.users--;
+}
+
+/**
+ * ccs_put_group - Drop reference on "struct ccs_group".
+ *
+ * @group: Pointer to "struct ccs_group". Maybe NULL.
+ *
+ * Returns nothing.
+ */
+static inline void ccs_put_group(struct ccs_group *group)
+{
+ if (group)
+ group->head.users--;
+}
+
+/**
+ * ccs_put_name - Drop reference on "struct ccs_name".
+ *
+ * @name: Pointer to "struct ccs_path_info". Maybe NULL.
+ *
+ * Returns nothing.
+ */
+static inline void ccs_put_name(const struct ccs_path_info *name)
+{
+ if (name)
+ container_of(name, struct ccs_name, entry)->head.users--;
+}
+
+/**
+ * ccs_put_name_union - Drop reference on "struct ccs_name_union".
+ *
+ * @ptr: Pointer to "struct ccs_name_union".
+ *
+ * Returns nothing.
+ */
+static void ccs_put_name_union(struct ccs_name_union *ptr)
+{
+ ccs_put_group(ptr->group);
+ ccs_put_name(ptr->filename);
+}
+
+/**
+ * ccs_put_number_union - Drop reference on "struct ccs_number_union".
+ *
+ * @ptr: Pointer to "struct ccs_number_union".
+ *
+ * Returns nothing.
+ */
+static void ccs_put_number_union(struct ccs_number_union *ptr)
+{
+ ccs_put_group(ptr->group);
+}
+
+/**
+ * ccs_del_condition - Delete members in "struct ccs_condition".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
+static void ccs_del_condition(struct list_head *element)
+{
+ struct ccs_condition *cond = container_of(element, typeof(*cond),
+ head.list);
+ const u16 condc = cond->condc;
+ const u16 numbers_count = cond->numbers_count;
+ const u16 names_count = cond->names_count;
+ const u16 argc = cond->argc;
+ const u16 envc = cond->envc;
+ unsigned int i;
+ const struct ccs_condition_element *condp
+ = (const struct ccs_condition_element *) (cond + 1);
+ struct ccs_number_union *numbers_p
+ = (struct ccs_number_union *) (condp + condc);
+ struct ccs_name_union *names_p
+ = (struct ccs_name_union *) (numbers_p + numbers_count);
+ const struct ccs_argv *argv
+ = (const struct ccs_argv *) (names_p + names_count);
+ const struct ccs_envp *envp
+ = (const struct ccs_envp *) (argv + argc);
+ for (i = 0; i < numbers_count; i++)
+ ccs_put_number_union(numbers_p++);
+ for (i = 0; i < names_count; i++)
+ ccs_put_name_union(names_p++);
+ for (i = 0; i < argc; argv++, i++)
+ ccs_put_name(argv->value);
+ for (i = 0; i < envc; envp++, i++) {
+ ccs_put_name(envp->name);
+ ccs_put_name(envp->value);
+ }
+ ccs_put_name(cond->transit);
+}
+
+/**
+ * ccs_yesno - Return "yes" or "no".
+ *
+ * @value: Bool value.
+ *
+ * Returns "yes" if @value is not 0, "no" otherwise.
+ */
+static const char *ccs_yesno(const unsigned int value)
+{
+ return value ? "yes" : "no";
+}
+
+/**
+ * ccs_same_name_union - Check for duplicated "struct ccs_name_union" entry.
+ *
+ * @a: Pointer to "struct ccs_name_union".
+ * @b: Pointer to "struct ccs_name_union".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static inline bool ccs_same_name_union(const struct ccs_name_union *a,
+ const struct ccs_name_union *b)
+{
+ return a->filename == b->filename && a->group == b->group;
+}
+
+/**
+ * ccs_same_number_union - Check for duplicated "struct ccs_number_union" entry.
+ *
+ * @a: Pointer to "struct ccs_number_union".
+ * @b: Pointer to "struct ccs_number_union".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static inline bool ccs_same_number_union(const struct ccs_number_union *a,
+ const struct ccs_number_union *b)
+{
+ return a->values[0] == b->values[0] && a->values[1] == b->values[1] &&
+ a->group == b->group && a->value_type[0] == b->value_type[0] &&
+ a->value_type[1] == b->value_type[1];
+}
+
+/**
+ * ccs_same_ipaddr_union - Check for duplicated "struct ccs_ipaddr_union" entry.
+ *
+ * @a: Pointer to "struct ccs_ipaddr_union".
+ * @b: Pointer to "struct ccs_ipaddr_union".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static inline bool ccs_same_ipaddr_union(const struct ccs_ipaddr_union *a,
+ const struct ccs_ipaddr_union *b)
+{
+ return !memcmp(a->ip, b->ip, sizeof(a->ip)) && a->group == b->group &&
+ a->is_ipv6 == b->is_ipv6;
+}
+
+/**
+ * ccs_partial_name_hash - Hash name.
+ *
+ * @c: A unsigned long value.
+ * @prevhash: A previous hash value.
+ *
+ * Returns new hash value.
+ *
+ * This function is copied from partial_name_hash() in the kernel source.
+ */
+static inline unsigned long ccs_partial_name_hash(unsigned long c,
+ unsigned long prevhash)
+{
+ return (prevhash + (c << 4) + (c >> 4)) * 11;
+}
+
+/**
+ * ccs_full_name_hash - Hash full name.
+ *
+ * @name: Pointer to "const unsigned char".
+ * @len: Length of @name in byte.
+ *
+ * Returns hash value.
+ *
+ * This function is copied from full_name_hash() in the kernel source.
+ */
+static inline unsigned int ccs_full_name_hash(const unsigned char *name,
+ unsigned int len)
+{
+ unsigned long hash = 0;
+ while (len--)
+ hash = ccs_partial_name_hash(*name++, hash);
+ return (unsigned int) hash;
+}
+
+/**
+ * ccs_get_name - Allocate memory for string data.
+ *
+ * @name: The string to store into the permernent memory.
+ *
+ * Returns pointer to "struct ccs_path_info" on success, abort otherwise.
+ */
+static const struct ccs_path_info *ccs_get_name(const char *name)
+{
+ struct ccs_name *ptr;
+ unsigned int hash;
+ int len;
+ int allocated_len;
+ struct list_head *head;
+
+ if (!name)
+ name = "";
+ len = strlen(name) + 1;
+ hash = ccs_full_name_hash((const unsigned char *) name, len - 1);
+ head = &ccs_name_list[hash % CCS_MAX_HASH];
+ list_for_each_entry(ptr, head, head.list) {
+ if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name))
+ continue;
+ ptr->head.users++;
+ goto out;
+ }
+ allocated_len = sizeof(*ptr) + len;
+ ptr = ccs_malloc(allocated_len);
+ ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
+ memmove((char *) ptr->entry.name, name, len);
+ ptr->head.users = 1;
+ ccs_fill_path_info(&ptr->entry);
+ ptr->size = allocated_len;
+ list_add_tail(&ptr->head.list, head);
+out:
+ return &ptr->entry;
+}
+
+
+/**
+ * ccs_commit_ok - Allocate memory and check memory quota.
+ *
+ * @data: Data to copy from.
+ * @size: Size in byte.
+ *
+ * Returns pointer to allocated memory on success, abort otherwise.
+ * @data is zero-cleared on success.
+ */
+static void *ccs_commit_ok(void *data, const unsigned int size)
+{
+ void *ptr = ccs_malloc(size);
+ memmove(ptr, data, size);
+ memset(data, 0, size);
+ return ptr;
+}
+
+/**
+ * ccs_permstr - Find permission keywords.
+ *
+ * @string: String representation for permissions in foo/bar/buz format.
+ * @keyword: Keyword to find from @string/
+ *
+ * Returns ture if @keyword was found in @string, false otherwise.
+ *
+ * This function assumes that strncmp(w1, w2, strlen(w1)) != 0 if w1 != w2.
+ */
+static bool ccs_permstr(const char *string, const char *keyword)
+{
+ const char *cp = strstr(string, keyword);
+ if (cp)
+ return cp == string || *(cp - 1) == '/';
+ return false;
+}
+
+/**
+ * ccs_read_token - Read a word from a line.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ *
+ * Returns a word on success, "" otherwise.
+ *
+ * To allow the caller to skip NULL check, this function returns "" rather than
+ * NULL if there is no more words to read.
+ */
+static char *ccs_read_token(struct ccs_acl_param *param)
+{
+ char *pos = param->data;
+ char *del = strchr(pos, ' ');
+ if (del)
+ *del++ = '\0';
+ else
+ del = pos + strlen(pos);
+ param->data = del;
+ return pos;
+}
+
+/**
+ * ccs_get_domainname - Read a domainname from a line.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ *
+ * Returns a domainname on success, NULL otherwise.
+ */
+static const struct ccs_path_info *ccs_get_domainname
+(struct ccs_acl_param *param)
+{
+ char *start = param->data;
+ char *pos = start;
+ while (*pos) {
+ if (*pos++ != ' ' || *pos++ == '/')
+ continue;
+ pos -= 2;
+ *pos++ = '\0';
+ break;
+ }
+ param->data = pos;
+ if (ccs_correct_domain(start))
+ return ccs_get_name(start);
+ return NULL;
+}
+
+/**
+ * ccs_get_group - Allocate memory for "struct ccs_path_group"/"struct ccs_number_group"/"struct ccs_address_group".
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ * @list: List to use.
+ *
+ * Returns pointer to "struct ccs_group" on success, NULL otherwise.
+ */
+static struct ccs_group *ccs_get_group(struct ccs_acl_param *param,
+ struct list_head *list)
+{
+ struct ccs_group e = { };
+ struct ccs_group *group = NULL;
+ const char *group_name = ccs_read_token(param);
+ bool found = false;
+ if (!ccs_correct_word(group_name))
+ return NULL;
+ e.ns = param->ns;
+ e.group_name = ccs_get_name(group_name);
+ list_for_each_entry(group, list, head.list) {
+ if (e.ns != group->ns || e.group_name != group->group_name)
+ continue;
+ group->head.users++;
+ found = true;
+ break;
+ }
+ if (!found) {
+ struct ccs_group *entry = ccs_commit_ok(&e, sizeof(e));
+ INIT_LIST_HEAD(&entry->member_list);
+ entry->head.users = 1;
+ list_add_tail(&entry->head.list, list);
+ group = entry;
+ found = true;
+ }
+ ccs_put_name(e.group_name);
+ return found ? group : NULL;
+}
+
+/**
+ * ccs_parse_ulong - Parse an "unsigned long" value.
+ *
+ * @result: Pointer to "unsigned long".
+ * @str: Pointer to string to parse.
+ *
+ * Returns one of values in "enum ccs_value_type".
+ *
+ * The @src is updated to point the first character after the value
+ * on success.
+ */
+static u8 ccs_parse_ulong(unsigned long *result, char **str)
+{
+ const char *cp = *str;
+ char *ep;
+ int base = 10;
+ if (*cp == '0') {
+ char c = *(cp + 1);
+ if (c == 'x' || c == 'X') {
+ base = 16;
+ cp += 2;
+ } else if (c >= '0' && c <= '7') {
+ base = 8;
+ cp++;
+ }
+ }
+ *result = strtoul(cp, &ep, base);
+ if (cp == ep)
+ return CCS_VALUE_TYPE_INVALID;
+ *str = ep;
+ switch (base) {
+ case 16:
+ return CCS_VALUE_TYPE_HEXADECIMAL;
+ case 8:
+ return CCS_VALUE_TYPE_OCTAL;
+ default:
+ return CCS_VALUE_TYPE_DECIMAL;
+ }
+}
+
+/**
+ * ccs_parse_name_union - Parse a ccs_name_union.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ * @ptr: Pointer to "struct ccs_name_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool ccs_parse_name_union(struct ccs_acl_param *param,
+ struct ccs_name_union *ptr)
+{
+ char *filename;
+ if (param->data[0] == '@') {
+ param->data++;
+ ptr->group = ccs_get_group(param, &ccs_path_group);
+ return ptr->group != NULL;
+ }
+ filename = ccs_read_token(param);
+ if (!ccs_correct_word(filename))
+ return false;
+ ptr->filename = ccs_get_name(filename);
+ return true;
+}
+
+/**
+ * ccs_parse_number_union - Parse a ccs_number_union.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ * @ptr: Pointer to "struct ccs_number_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool ccs_parse_number_union(struct ccs_acl_param *param,
+ struct ccs_number_union *ptr)
+{
+ char *data;
+ u8 type;
+ unsigned long v;
+ memset(ptr, 0, sizeof(*ptr));
+ if (param->data[0] == '@') {
+ param->data++;
+ ptr->group = ccs_get_group(param, &ccs_number_group);
+ return ptr->group != NULL;
+ }
+ data = ccs_read_token(param);
+ type = ccs_parse_ulong(&v, &data);
+ if (type == CCS_VALUE_TYPE_INVALID)
+ return false;
+ ptr->values[0] = v;
+ ptr->value_type[0] = type;
+ if (!*data) {
+ ptr->values[1] = v;
+ ptr->value_type[1] = type;
+ return true;
+ }
+ if (*data++ != '-')
+ return false;
+ type = ccs_parse_ulong(&v, &data);
+ if (type == CCS_VALUE_TYPE_INVALID || *data || ptr->values[0] > v)
+ return false;
+ ptr->values[1] = v;
+ ptr->value_type[1] = type;
+ return true;
+}
+
+/**
+ * ccs_find_domain2 - Find a domain by the given name.
+ *
+ * @domainname: The domainname to find.
+ *
+ * Returns pointer to "struct ccs_domain2_info" if found, NULL otherwise.
+ */
+static struct ccs_domain2_info *ccs_find_domain2(const char *domainname)
+{
+ struct ccs_domain2_info *domain;
+ struct ccs_path_info name;
+ name.name = domainname;
+ ccs_fill_path_info(&name);
+ list_for_each_entry(domain, &ccs_domain_list, list) {
+ if (!domain->is_deleted &&
+ !ccs_pathcmp(&name, domain->domainname))
+ return domain;
+ }
+ return NULL;
+}
+
+static int client_fd = EOF;
+
+static void cprintf(const char *fmt, ...)
+ __attribute__ ((format(printf, 1, 2)));
+
+/**
+ * cprintf - printf() over socket.
+ *
+ * @fmt: The printf()'s format string, followed by parameters.
+ *
+ * Returns nothing.
+ */
+static void cprintf(const char *fmt, ...)
+{
+ va_list args;
+ static char *buffer = NULL;
+ static unsigned int buffer_len = 0;
+ static unsigned int buffer_pos = 0;
+ int len;
+ if (head.reset) {
+ head.reset = false;
+ buffer_pos = 0;
+ }
+ while (1) {
+ va_start(args, fmt);
+ len = vsnprintf(buffer + buffer_pos, buffer_len - buffer_pos,
+ fmt, args);
+ va_end(args);
+ if (len < 0)
+ _exit(1);
+ if (buffer_pos + len < buffer_len) {
+ buffer_pos += len;
+ break;
+ }
+ buffer_len = buffer_pos + len + 4096;
+ buffer = ccs_realloc(buffer, buffer_len);
+ }
+ if (len && buffer_pos < 1048576)
+ return;
+ /*
+ * Reader might close connection without reading until EOF.
+ * In that case, we should not call _exit() because offline daemon does
+ * not call fork() for each accept()ed socket connection.
+ */
+ if (write(client_fd, buffer, buffer_pos) != buffer_pos) {
+ close(client_fd);
+ client_fd = EOF;
+ }
+ buffer_pos = 0;
+}
+
+/**
+ * ccs_update_policy - Update an entry for exception policy.
+ *
+ * @new_entry: Pointer to "struct ccs_acl_info".
+ * @size: Size of @new_entry in bytes.
+ * @param: Pointer to "struct ccs_acl_param".
+ * @check_duplicate: Callback function to find duplicated entry.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_update_policy(struct ccs_acl_head *new_entry, const int size,
+ struct ccs_acl_param *param,
+ bool (*check_duplicate)
+ (const struct ccs_acl_head *,
+ const struct ccs_acl_head *))
+{
+ int error = param->is_delete ? -ENOENT : -ENOMEM;
+ struct ccs_acl_head *entry;
+ struct list_head *list = param->list;
+ list_for_each_entry(entry, list, list) {
+ if (!check_duplicate(entry, new_entry))
+ continue;
+ entry->is_deleted = param->is_delete;
+ error = 0;
+ break;
+ }
+ if (error && !param->is_delete) {
+ entry = ccs_commit_ok(new_entry, size);
+ list_add_tail(&entry->list, list);
+ error = 0;
+ }
+ return error;
+}
+
+/* List of "struct ccs_condition". */
+static LIST_HEAD(ccs_condition_list);
+
+/**
+ * ccs_get_dqword - ccs_get_name() for a quoted string.
+ *
+ * @start: String to save.
+ *
+ * Returns pointer to "struct ccs_path_info" on success, NULL otherwise.
+ */
+static const struct ccs_path_info *ccs_get_dqword(char *start)
+{
+ char *cp = start + strlen(start) - 1;
+ if (cp == start || *start++ != '"' || *cp != '"')
+ return NULL;
+ *cp = '\0';
+ if (*start && !ccs_correct_word(start))
+ return NULL;
+ return ccs_get_name(start);
+}
+
+/**
+ * ccs_parse_name_union_quoted - Parse a quoted word.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ * @ptr: Pointer to "struct ccs_name_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool ccs_parse_name_union_quoted(struct ccs_acl_param *param,
+ struct ccs_name_union *ptr)
+{
+ char *filename = param->data;
+ if (*filename == '@')
+ return ccs_parse_name_union(param, ptr);
+ ptr->filename = ccs_get_dqword(filename);
+ return ptr->filename != NULL;
+}
+
+/**
+ * ccs_parse_argv - Parse an argv[] condition part.
+ *
+ * @left: Lefthand value.
+ * @right: Righthand value.
+ * @argv: Pointer to "struct ccs_argv".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool ccs_parse_argv(char *left, char *right, struct ccs_argv *argv)
+{
+ if (ccs_parse_ulong(&argv->index, &left) != CCS_VALUE_TYPE_DECIMAL ||
+ *left++ != ']' || *left)
+ return false;
+ argv->value = ccs_get_dqword(right);
+ return argv->value != NULL;
+}
+
+/**
+ * ccs_parse_envp - Parse an envp[] condition part.
+ *
+ * @left: Lefthand value.
+ * @right: Righthand value.
+ * @envp: Pointer to "struct ccs_envp".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool ccs_parse_envp(char *left, char *right, struct ccs_envp *envp)
+{
+ const struct ccs_path_info *name;
+ const struct ccs_path_info *value;
+ char *cp = left + strlen(left) - 1;
+ if (*cp-- != ']' || *cp != '"')
+ goto out;
+ *cp = '\0';
+ if (!ccs_correct_word(left))
+ goto out;
+ name = ccs_get_name(left);
+ if (!strcmp(right, "NULL")) {
+ value = NULL;
+ } else {
+ value = ccs_get_dqword(right);
+ if (!value) {
+ ccs_put_name(name);
+ goto out;
+ }
+ }
+ envp->name = name;
+ envp->value = value;
+ return true;
+out:
+ return false;
+}
+
+/**
+ * ccs_same_condition - Check for duplicated "struct ccs_condition" entry.
+ *
+ * @a: Pointer to "struct ccs_condition".
+ * @b: Pointer to "struct ccs_condition".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static inline bool ccs_same_condition(const struct ccs_condition *a,
+ const struct ccs_condition *b)
+{
+ return a->size == b->size && a->condc == b->condc &&
+ a->numbers_count == b->numbers_count &&
+ a->names_count == b->names_count &&
+ a->argc == b->argc && a->envc == b->envc &&
+ a->grant_log == b->grant_log && a->transit == b->transit &&
+ !memcmp(a + 1, b + 1, a->size - sizeof(*a));
+}
+
+/**
+ * ccs_condition_type - Get condition type.
+ *
+ * @word: Keyword string.
+ *
+ * Returns one of values in "enum ccs_conditions_index" on success,
+ * CCS_MAX_CONDITION_KEYWORD otherwise.
+ */
+static u8 ccs_condition_type(const char *word)
+{
+ u8 i;
+ for (i = 0; i < CCS_MAX_CONDITION_KEYWORD; i++) {
+ if (!strcmp(word, ccs_condition_keyword[i]))
+ break;
+ }
+ return i;
+}
+
+/* Define this to enable debug mode. */
+/* #define DEBUG_CONDITION */
+
+#ifdef DEBUG_CONDITION
+#define dprintk printk
+#else
+#define dprintk(...) do { } while (0)
+#endif
+
+/**
+ * ccs_commit_condition - Commit "struct ccs_condition".
+ *
+ * @entry: Pointer to "struct ccs_condition".
+ *
+ * Returns pointer to "struct ccs_condition" on success, NULL otherwise.
+ *
+ * This function merges duplicated entries. This function returns NULL if
+ * @entry is not duplicated but memory quota for policy has exceeded.
+ */
+static struct ccs_condition *ccs_commit_condition(struct ccs_condition *entry)
+{
+ struct ccs_condition *ptr;
+ bool found = false;
+ list_for_each_entry(ptr, &ccs_condition_list, head.list) {
+ if (!ccs_same_condition(ptr, entry))
+ continue;
+ /* Same entry found. Share this entry. */
+ ptr->head.users++;
+ found = true;
+ break;
+ }
+ if (!found) {
+ if (entry) {
+ entry->head.users = 1;
+ list_add(&entry->head.list, &ccs_condition_list);
+ } else {
+ found = true;
+ ptr = NULL;
+ }
+ }
+ if (found) {
+ ccs_del_condition(&entry->head.list);
+ free(entry);
+ entry = ptr;
+ }
+ return entry;
+}
+
+/**
+ * ccs_get_condition - Parse condition part.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ *
+ * Returns pointer to "struct ccs_condition" on success, NULL otherwise.
+ */
+static struct ccs_condition *ccs_get_condition(struct ccs_acl_param *param)
+{
+ struct ccs_condition *entry = NULL;
+ struct ccs_condition_element *condp = NULL;
+ struct ccs_number_union *numbers_p = NULL;
+ struct ccs_name_union *names_p = NULL;
+ struct ccs_argv *argv = NULL;
+ struct ccs_envp *envp = NULL;
+ struct ccs_condition e = { };
+ char * const start_of_string = param->data;
+ char * const end_of_string = start_of_string + strlen(start_of_string);
+ char *pos;
+rerun:
+ pos = start_of_string;
+ while (1) {
+ u8 left = -1;
+ u8 right = -1;
+ char *left_word = pos;
+ char *cp;
+ char *right_word;
+ bool is_not;
+ if (!*left_word)
+ break;
+ /*
+ * Since left-hand condition does not allow use of "path_group"
+ * or "number_group" and environment variable's names do not
+ * accept '=', it is guaranteed that the original line consists
+ * of one or more repetition of $left$operator$right blocks
+ * where "$left is free from '=' and ' '" and "$operator is
+ * either '=' or '!='" and "$right is free from ' '".
+ * Therefore, we can reconstruct the original line at the end
+ * of dry run even if we overwrite $operator with '\0'.
+ */
+ cp = strchr(pos, ' ');
+ if (cp) {
+ *cp = '\0'; /* Will restore later. */
+ pos = cp + 1;
+ } else {
+ pos = "";
+ }
+ right_word = strchr(left_word, '=');
+ if (!right_word || right_word == left_word)
+ goto out;
+ is_not = *(right_word - 1) == '!';
+ if (is_not)
+ *(right_word++ - 1) = '\0'; /* Will restore later. */
+ else if (*(right_word + 1) != '=')
+ *right_word++ = '\0'; /* Will restore later. */
+ else
+ goto out;
+ dprintk(KERN_WARNING "%u: <%s>%s=<%s>\n", __LINE__, left_word,
+ is_not ? "!" : "", right_word);
+ if (!strcmp(left_word, "grant_log")) {
+ if (entry) {
+ if (is_not ||
+ entry->grant_log != CCS_GRANTLOG_AUTO)
+ goto out;
+ else if (!strcmp(right_word, "yes"))
+ entry->grant_log = CCS_GRANTLOG_YES;
+ else if (!strcmp(right_word, "no"))
+ entry->grant_log = CCS_GRANTLOG_NO;
+ else
+ goto out;
+ }
+ continue;
+ }
+ if (!strcmp(left_word, "auto_domain_transition")) {
+ if (entry) {
+ if (is_not || entry->transit)
+ goto out;
+ entry->transit = ccs_get_dqword(right_word);
+ if (!entry->transit ||
+ (entry->transit->name[0] != '/' &&
+ !ccs_domain_def(entry->transit->name)))
+ goto out;
+ }
+ continue;
+ }
+ if (!strncmp(left_word, "exec.argv[", 10)) {
+ if (!argv) {
+ e.argc++;
+ e.condc++;
+ } else {
+ e.argc--;
+ e.condc--;
+ left = CCS_ARGV_ENTRY;
+ argv->is_not = is_not;
+ if (!ccs_parse_argv(left_word + 10,
+ right_word, argv++))
+ goto out;
+ }
+ goto store_value;
+ }
+ if (!strncmp(left_word, "exec.envp[\"", 11)) {
+ if (!envp) {
+ e.envc++;
+ e.condc++;
+ } else {
+ e.envc--;
+ e.condc--;
+ left = CCS_ENVP_ENTRY;
+ envp->is_not = is_not;
+ if (!ccs_parse_envp(left_word + 11,
+ right_word, envp++))
+ goto out;
+ }
+ goto store_value;
+ }
+ left = ccs_condition_type(left_word);
+ dprintk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__, left_word,
+ left);
+ if (left == CCS_MAX_CONDITION_KEYWORD) {
+ if (!numbers_p) {
+ e.numbers_count++;
+ } else {
+ e.numbers_count--;
+ left = CCS_NUMBER_UNION;
+ param->data = left_word;
+ if (*left_word == '@' ||
+ !ccs_parse_number_union(param,
+ numbers_p++))
+ goto out;
+ }
+ }
+ if (!condp)
+ e.condc++;
+ else
+ e.condc--;
+ if (left == CCS_EXEC_REALPATH || left == CCS_SYMLINK_TARGET) {
+ if (!names_p) {
+ e.names_count++;
+ } else {
+ e.names_count--;
+ right = CCS_NAME_UNION;
+ param->data = right_word;
+ if (!ccs_parse_name_union_quoted(param,
+ names_p++))
+ goto out;
+ }
+ goto store_value;
+ }
+ right = ccs_condition_type(right_word);
+ if (right == CCS_MAX_CONDITION_KEYWORD) {
+ if (!numbers_p) {
+ e.numbers_count++;
+ } else {
+ e.numbers_count--;
+ right = CCS_NUMBER_UNION;
+ param->data = right_word;
+ if (!ccs_parse_number_union(param,
+ numbers_p++))
+ goto out;
+ }
+ }
+store_value:
+ if (!condp) {
+ dprintk(KERN_WARNING "%u: dry_run left=%u right=%u "
+ "match=%u\n", __LINE__, left, right, !is_not);
+ continue;
+ }
+ condp->left = left;
+ condp->right = right;
+ condp->equals = !is_not;
+ dprintk(KERN_WARNING "%u: left=%u right=%u match=%u\n",
+ __LINE__, condp->left, condp->right,
+ condp->equals);
+ condp++;
+ }
+ dprintk(KERN_INFO "%u: cond=%u numbers=%u names=%u ac=%u ec=%u\n",
+ __LINE__, e.condc, e.numbers_count, e.names_count, e.argc,
+ e.envc);
+ if (entry)
+ return ccs_commit_condition(entry);
+ e.size = sizeof(*entry)
+ + e.condc * sizeof(struct ccs_condition_element)
+ + e.numbers_count * sizeof(struct ccs_number_union)
+ + e.names_count * sizeof(struct ccs_name_union)
+ + e.argc * sizeof(struct ccs_argv)
+ + e.envc * sizeof(struct ccs_envp);
+ entry = ccs_malloc(e.size);
+ *entry = e;
+ condp = (struct ccs_condition_element *) (entry + 1);
+ numbers_p = (struct ccs_number_union *) (condp + e.condc);
+ names_p = (struct ccs_name_union *) (numbers_p + e.numbers_count);
+ argv = (struct ccs_argv *) (names_p + e.names_count);
+ envp = (struct ccs_envp *) (argv + e.argc);
+ {
+ bool flag = false;
+ for (pos = start_of_string; pos < end_of_string; pos++) {
+ if (*pos)
+ continue;
+ if (flag) /* Restore " ". */
+ *pos = ' ';
+ else if (*(pos + 1) == '=') /* Restore "!=". */
+ *pos = '!';
+ else /* Restore "=". */
+ *pos = '=';
+ flag = !flag;
+ }
+ }
+ goto rerun;
+out:
+ dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__);
+ if (entry) {
+ ccs_del_condition(&entry->head.list);
+ free(entry);
+ }
+ return NULL;
+}
+
+/**
+ * ccs_same_acl_head - Check for duplicated "struct ccs_acl_info" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_info".
+ * @b: Pointer to "struct ccs_acl_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static inline bool ccs_same_acl_head(const struct ccs_acl_info *a,
+ const struct ccs_acl_info *b)
+{
+ return a->type == b->type && a->cond == b->cond;
+}
+
+/**
+ * ccs_update_domain - Update an entry for domain policy.
+ *
+ * @new_entry: Pointer to "struct ccs_acl_info".
+ * @size: Size of @new_entry in bytes.
+ * @param: Pointer to "struct ccs_acl_param".
+ * @check_duplicate: Callback function to find duplicated entry.
+ * @merge_duplicate: Callback function to merge duplicated entry. Maybe NULL.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_update_domain(struct ccs_acl_info *new_entry, const int size,
+ struct ccs_acl_param *param,
+ bool (*check_duplicate)
+ (const struct ccs_acl_info *,
+ const struct ccs_acl_info *),
+ bool (*merge_duplicate)
+ (struct ccs_acl_info *, struct ccs_acl_info *,
+ const bool))
+{
+ const bool is_delete = param->is_delete;
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ struct ccs_acl_info *entry;
+ struct list_head * const list = param->list;
+ if (param->data[0]) {
+ new_entry->cond = ccs_get_condition(param);
+ if (!new_entry->cond)
+ return -EINVAL;
+ }
+ list_for_each_entry(entry, list, list) {
+ if (!ccs_same_acl_head(entry, new_entry) ||
+ !check_duplicate(entry, new_entry))
+ continue;
+ if (merge_duplicate)
+ entry->is_deleted = merge_duplicate(entry, new_entry,
+ is_delete);
+ else
+ entry->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (error && !is_delete) {
+ entry = ccs_commit_ok(new_entry, size);
+ list_add_tail(&entry->list, list);
+ error = 0;
+ }
+ ccs_put_condition(new_entry->cond);
+ return error;
+}
+
+/**
+ * ccs_same_transition_control - Check for duplicated "struct ccs_transition_control" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_head".
+ * @b: Pointer to "struct ccs_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool ccs_same_transition_control(const struct ccs_acl_head *a,
+ const struct ccs_acl_head *b)
+{
+ const struct ccs_transition_control *p1 = container_of(a, typeof(*p1),
+ head);
+ const struct ccs_transition_control *p2 = container_of(b, typeof(*p2),
+ head);
+ return p1->type == p2->type && p1->is_last_name == p2->is_last_name
+ && p1->domainname == p2->domainname
+ && p1->program == p2->program && p1->ns == p2->ns;
+}
+
+/**
+ * ccs_write_transition_control - Write "struct ccs_transition_control" list.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ * @type: Type of this entry.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_write_transition_control(struct ccs_acl_param *param,
+ const u8 type)
+{
+ struct ccs_transition_control e = { .type = type, .ns = param->ns };
+ int error = param->is_delete ? -ENOENT : -ENOMEM;
+ char *program = param->data;
+ char *domainname = strstr(program, " from ");
+ if (domainname) {
+ *domainname = '\0';
+ domainname += 6;
+ } else if (type == CCS_TRANSITION_CONTROL_NO_KEEP ||
+ type == CCS_TRANSITION_CONTROL_KEEP) {
+ domainname = program;
+ program = NULL;
+ }
+ if (program && strcmp(program, "any")) {
+ if (!ccs_correct_path(program))
+ return -EINVAL;
+ e.program = ccs_get_name(program);
+ }
+ if (domainname && strcmp(domainname, "any")) {
+ if (!ccs_correct_domain(domainname)) {
+ if (!ccs_correct_path(domainname))
+ goto out;
+ e.is_last_name = true;
+ }
+ e.domainname = ccs_get_name(domainname);
+ }
+ param->list = &ccs_transition_list;
+ error = ccs_update_policy(&e.head, sizeof(e), param,
+ ccs_same_transition_control);
+out:
+ ccs_put_name(e.domainname);
+ ccs_put_name(e.program);
+ return error;
+}
+
+/**
+ * ccs_same_aggregator - Check for duplicated "struct ccs_aggregator" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_head".
+ * @b: Pointer to "struct ccs_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool ccs_same_aggregator(const struct ccs_acl_head *a,
+ const struct ccs_acl_head *b)
+{
+ const struct ccs_aggregator *p1 = container_of(a, typeof(*p1), head);
+ const struct ccs_aggregator *p2 = container_of(b, typeof(*p2), head);
+ return p1->original_name == p2->original_name &&
+ p1->aggregated_name == p2->aggregated_name && p1->ns == p2->ns;
+}
+
+/**
+ * ccs_write_aggregator - Write "struct ccs_aggregator" list.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_write_aggregator(struct ccs_acl_param *param)
+{
+ struct ccs_aggregator e = { .ns = param->ns };
+ int error = param->is_delete ? -ENOENT : -ENOMEM;
+ const char *original_name = ccs_read_token(param);
+ const char *aggregated_name = ccs_read_token(param);
+ if (!ccs_correct_word(original_name) ||
+ !ccs_correct_path(aggregated_name))
+ return -EINVAL;
+ e.original_name = ccs_get_name(original_name);
+ e.aggregated_name = ccs_get_name(aggregated_name);
+ if (e.aggregated_name->is_patterned) /* No patterns allowed. */
+ goto out;
+ param->list = &ccs_aggregator_list;
+ error = ccs_update_policy(&e.head, sizeof(e), param,
+ ccs_same_aggregator);
+out:
+ ccs_put_name(e.original_name);
+ ccs_put_name(e.aggregated_name);
+ return error;
+}
+
+/* Domain create handler. */
+
+/**
+ * ccs_find_namespace - Find specified namespace.
+ *
+ * @name: Name of namespace to find.
+ * @len: Length of @name.
+ *
+ * Returns pointer to "struct ccs_policy_namespace" if found, NULL otherwise.
+ */
+static struct ccs_policy_namespace *ccs_find_namespace(const char *name,
+ const unsigned int len)
+{
+ struct ccs_policy_namespace *ns;
+ list_for_each_entry(ns, &ccs_namespace_list, namespace_list) {
+ if (strncmp(name, ns->name, len) ||
+ (name[len] && name[len] != ' '))
+ continue;
+ return ns;
+ }
+ return NULL;
+}
+
+/**
+ * ccs_assign_namespace - Create a new namespace.
+ *
+ * @domainname: Name of namespace to create.
+ *
+ * Returns pointer to "struct ccs_policy_namespace" on success, NULL otherwise.
+ */
+static struct ccs_policy_namespace *ccs_assign_namespace(const char *domainname)
+{
+ struct ccs_policy_namespace *ptr;
+ struct ccs_policy_namespace *entry;
+ const char *cp = domainname;
+ unsigned int len = 0;
+ while (*cp && *cp++ != ' ')
+ len++;
+ ptr = ccs_find_namespace(domainname, len);
+ if (ptr)
+ return ptr;
+ if (len >= CCS_EXEC_TMPSIZE - 10 || !ccs_domain_def(domainname))
+ return NULL;
+ entry = ccs_malloc(sizeof(*entry) + len + 1);
+ {
+ char *name = (char *) (entry + 1);
+ memmove(name, domainname, len);
+ name[len] = '\0';
+ entry->name = name;
+ }
+ entry->profile_version = 20100903;
+ for (len = 0; len < CCS_MAX_ACL_GROUPS; len++)
+ INIT_LIST_HEAD(&entry->acl_group[len]);
+ ccs_namespace_enabled = !list_empty(&ccs_namespace_list);
+ list_add_tail(&entry->namespace_list, &ccs_namespace_list);
+ return entry;
+}
+
+/**
+ * ccs_assign_domain2 - Create a domain or a namespace.
+ *
+ * @domainname: The name of domain.
+ *
+ * Returns pointer to "struct ccs_domain2_info" on success, NULL otherwise.
+ */
+static struct ccs_domain2_info *ccs_assign_domain2(const char *domainname)
+{
+ struct ccs_domain2_info e = { };
+ struct ccs_domain2_info *entry = ccs_find_domain2(domainname);
+ if (entry)
+ return entry;
+ /* Requested domain does not exist. */
+ /* Don't create requested domain if domainname is invalid. */
+ if (strlen(domainname) >= CCS_EXEC_TMPSIZE - 10 ||
+ !ccs_correct_domain(domainname))
+ return NULL;
+ e.domainname = ccs_get_name(domainname);
+ entry = ccs_commit_ok(&e, sizeof(e));
+ INIT_LIST_HEAD(&entry->acl_info_list);
+ list_add_tail(&entry->list, &ccs_domain_list);
+ ccs_put_name(e.domainname);
+ return entry;
+}
+
+/**
+ * ccs_same_path_acl - Check for duplicated "struct ccs_path_acl" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_info".
+ * @b: Pointer to "struct ccs_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
+static bool ccs_same_path_acl(const struct ccs_acl_info *a,
+ const struct ccs_acl_info *b)
+{
+ const struct ccs_path_acl *p1 = container_of(a, typeof(*p1), head);
+ const struct ccs_path_acl *p2 = container_of(b, typeof(*p2), head);
+ return ccs_same_name_union(&p1->name, &p2->name);
+}
+
+/**
+ * ccs_merge_path_acl - Merge duplicated "struct ccs_path_acl" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_info".
+ * @b: Pointer to "struct ccs_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
+static bool ccs_merge_path_acl(struct ccs_acl_info *a, struct ccs_acl_info *b,
+ const bool is_delete)
+{
+ u16 * const a_perm = &container_of(a, struct ccs_path_acl, head)->perm;
+ u16 perm = *a_perm;
+ const u16 b_perm = container_of(b, struct ccs_path_acl, head)->perm;
+ if (is_delete)
+ perm &= ~b_perm;
+ else
+ perm |= b_perm;
+ *a_perm = perm;
+ return !perm;
+}
+
+/**
+ * ccs_update_path_acl - Update "struct ccs_path_acl" list.
+ *
+ * @perm: Permission.
+ * @param: Pointer to "struct ccs_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_update_path_acl(const u16 perm, struct ccs_acl_param *param)
+{
+ struct ccs_path_acl e = {
+ .head.type = CCS_TYPE_PATH_ACL,
+ .perm = perm
+ };
+ int error;
+ if (!ccs_parse_name_union(param, &e.name))
+ error = -EINVAL;
+ else
+ error = ccs_update_domain(&e.head, sizeof(e), param,
+ ccs_same_path_acl,
+ ccs_merge_path_acl);
+ ccs_put_name_union(&e.name);
+ return error;
+}
+
+/**
+ * ccs_same_mkdev_acl - Check for duplicated "struct ccs_mkdev_acl" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_info".
+ * @b: Pointer to "struct ccs_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
+static bool ccs_same_mkdev_acl(const struct ccs_acl_info *a,
+ const struct ccs_acl_info *b)
+{
+ const struct ccs_mkdev_acl *p1 = container_of(a, typeof(*p1), head);
+ const struct ccs_mkdev_acl *p2 = container_of(b, typeof(*p2), head);
+ return ccs_same_name_union(&p1->name, &p2->name) &&
+ ccs_same_number_union(&p1->mode, &p2->mode) &&
+ ccs_same_number_union(&p1->major, &p2->major) &&
+ ccs_same_number_union(&p1->minor, &p2->minor);
+}
+
+/**
+ * ccs_merge_mkdev_acl - Merge duplicated "struct ccs_mkdev_acl" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_info".
+ * @b: Pointer to "struct ccs_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
+static bool ccs_merge_mkdev_acl(struct ccs_acl_info *a, struct ccs_acl_info *b,
+ const bool is_delete)
+{
+ u8 *const a_perm = &container_of(a, struct ccs_mkdev_acl, head)->perm;
+ u8 perm = *a_perm;
+ const u8 b_perm = container_of(b, struct ccs_mkdev_acl, head)->perm;
+ if (is_delete)
+ perm &= ~b_perm;
+ else
+ perm |= b_perm;
+ *a_perm = perm;
+ return !perm;
+}
+
+/**
+ * ccs_update_mkdev_acl - Update "struct ccs_mkdev_acl" list.
+ *
+ * @perm: Permission.
+ * @param: Pointer to "struct ccs_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_update_mkdev_acl(const u8 perm, struct ccs_acl_param *param)
+{
+ struct ccs_mkdev_acl e = {
+ .head.type = CCS_TYPE_MKDEV_ACL,
+ .perm = perm
+ };
+ int error;
+ if (!ccs_parse_name_union(param, &e.name) ||
+ !ccs_parse_number_union(param, &e.mode) ||
+ !ccs_parse_number_union(param, &e.major) ||
+ !ccs_parse_number_union(param, &e.minor))
+ error = -EINVAL;
+ else
+ error = ccs_update_domain(&e.head, sizeof(e), param,
+ ccs_same_mkdev_acl,
+ ccs_merge_mkdev_acl);
+ ccs_put_name_union(&e.name);
+ ccs_put_number_union(&e.mode);
+ ccs_put_number_union(&e.major);
+ ccs_put_number_union(&e.minor);
+ return error;
+}
+
+/**
+ * ccs_same_path2_acl - Check for duplicated "struct ccs_path2_acl" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_info".
+ * @b: Pointer to "struct ccs_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
+static bool ccs_same_path2_acl(const struct ccs_acl_info *a,
+ const struct ccs_acl_info *b)
+{
+ const struct ccs_path2_acl *p1 = container_of(a, typeof(*p1), head);
+ const struct ccs_path2_acl *p2 = container_of(b, typeof(*p2), head);
+ return ccs_same_name_union(&p1->name1, &p2->name1) &&
+ ccs_same_name_union(&p1->name2, &p2->name2);
+}
+
+/**
+ * ccs_merge_path2_acl - Merge duplicated "struct ccs_path2_acl" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_info".
+ * @b: Pointer to "struct ccs_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
+static bool ccs_merge_path2_acl(struct ccs_acl_info *a, struct ccs_acl_info *b,
+ const bool is_delete)
+{
+ u8 * const a_perm = &container_of(a, struct ccs_path2_acl, head)->perm;
+ u8 perm = *a_perm;
+ const u8 b_perm = container_of(b, struct ccs_path2_acl, head)->perm;
+ if (is_delete)
+ perm &= ~b_perm;
+ else
+ perm |= b_perm;
+ *a_perm = perm;
+ return !perm;
+}
+
+/**
+ * ccs_update_path2_acl - Update "struct ccs_path2_acl" list.
+ *
+ * @perm: Permission.
+ * @param: Pointer to "struct ccs_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_update_path2_acl(const u8 perm, struct ccs_acl_param *param)
+{
+ struct ccs_path2_acl e = {
+ .head.type = CCS_TYPE_PATH2_ACL,
+ .perm = perm
+ };
+ int error;
+ if (!ccs_parse_name_union(param, &e.name1) ||
+ !ccs_parse_name_union(param, &e.name2))
+ error = -EINVAL;
+ else
+ error = ccs_update_domain(&e.head, sizeof(e), param,
+ ccs_same_path2_acl,
+ ccs_merge_path2_acl);
+ ccs_put_name_union(&e.name1);
+ ccs_put_name_union(&e.name2);
+ return error;
+}
+
+/**
+ * ccs_same_path_number_acl - Check for duplicated "struct ccs_path_number_acl" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_info".
+ * @b: Pointer to "struct ccs_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
+static bool ccs_same_path_number_acl(const struct ccs_acl_info *a,
+ const struct ccs_acl_info *b)
+{
+ const struct ccs_path_number_acl *p1 = container_of(a, typeof(*p1),
+ head);
+ const struct ccs_path_number_acl *p2 = container_of(b, typeof(*p2),
+ head);
+ return ccs_same_name_union(&p1->name, &p2->name) &&
+ ccs_same_number_union(&p1->number, &p2->number);
+}
+
+/**
+ * ccs_merge_path_number_acl - Merge duplicated "struct ccs_path_number_acl" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_info".
+ * @b: Pointer to "struct ccs_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
+static bool ccs_merge_path_number_acl(struct ccs_acl_info *a,
+ struct ccs_acl_info *b,
+ const bool is_delete)
+{
+ u8 * const a_perm = &container_of(a, struct ccs_path_number_acl, head)
+ ->perm;
+ u8 perm = *a_perm;
+ const u8 b_perm = container_of(b, struct ccs_path_number_acl, head)
+ ->perm;
+ if (is_delete)
+ perm &= ~b_perm;
+ else
+ perm |= b_perm;
+ *a_perm = perm;
+ return !perm;
+}
+
+/**
+ * ccs_update_path_number_acl - Update create/mkdir/mkfifo/mksock/ioctl/chmod/chown/chgrp ACL.
+ *
+ * @perm: Permission.
+ * @param: Pointer to "struct ccs_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_update_path_number_acl(const u8 perm,
+ struct ccs_acl_param *param)
+{
+ struct ccs_path_number_acl e = {
+ .head.type = CCS_TYPE_PATH_NUMBER_ACL,
+ .perm = perm
+ };
+ int error;
+ if (!ccs_parse_name_union(param, &e.name) ||
+ !ccs_parse_number_union(param, &e.number))
+ error = -EINVAL;
+ else
+ error = ccs_update_domain(&e.head, sizeof(e), param,
+ ccs_same_path_number_acl,
+ ccs_merge_path_number_acl);
+ ccs_put_name_union(&e.name);
+ ccs_put_number_union(&e.number);
+ return error;
+}
+
+/**
+ * ccs_same_mount_acl - Check for duplicated "struct ccs_mount_acl" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_info".
+ * @b: Pointer to "struct ccs_acl_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool ccs_same_mount_acl(const struct ccs_acl_info *a,
+ const struct ccs_acl_info *b)
+{
+ const struct ccs_mount_acl *p1 = container_of(a, typeof(*p1), head);
+ const struct ccs_mount_acl *p2 = container_of(b, typeof(*p2), head);
+ return ccs_same_name_union(&p1->dev_name, &p2->dev_name) &&
+ ccs_same_name_union(&p1->dir_name, &p2->dir_name) &&
+ ccs_same_name_union(&p1->fs_type, &p2->fs_type) &&
+ ccs_same_number_union(&p1->flags, &p2->flags);
+}
+
+/**
+ * ccs_update_mount_acl - Write "struct ccs_mount_acl" list.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_update_mount_acl(struct ccs_acl_param *param)
+{
+ struct ccs_mount_acl e = { .head.type = CCS_TYPE_MOUNT_ACL };
+ int error;
+ if (!ccs_parse_name_union(param, &e.dev_name) ||
+ !ccs_parse_name_union(param, &e.dir_name) ||
+ !ccs_parse_name_union(param, &e.fs_type) ||
+ !ccs_parse_number_union(param, &e.flags))
+ error = -EINVAL;
+ else
+ error = ccs_update_domain(&e.head, sizeof(e), param,
+ ccs_same_mount_acl, NULL);
+ ccs_put_name_union(&e.dev_name);
+ ccs_put_name_union(&e.dir_name);
+ ccs_put_name_union(&e.fs_type);
+ ccs_put_number_union(&e.flags);
+ return error;
+}
+
+/**
+ * ccs_write_file - Update file related list.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_write_file(struct ccs_acl_param *param)
+{
+ u16 perm = 0;
+ u8 type;
+ const char *operation = ccs_read_token(param);
+ for (type = 0; type < CCS_MAX_PATH_OPERATION; type++)
+ if (ccs_permstr(operation, ccs_path_keyword[type]))
+ perm |= 1 << type;
+ if (perm)
+ return ccs_update_path_acl(perm, param);
+ for (type = 0; type < CCS_MAX_PATH2_OPERATION; type++)
+ if (ccs_permstr(operation, ccs_mac_keywords[ccs_pp2mac[type]]))
+ perm |= 1 << type;
+ if (perm)
+ return ccs_update_path2_acl(perm, param);
+ for (type = 0; type < CCS_MAX_PATH_NUMBER_OPERATION; type++)
+ if (ccs_permstr(operation, ccs_mac_keywords[ccs_pn2mac[type]]))
+ perm |= 1 << type;
+ if (perm)
+ return ccs_update_path_number_acl(perm, param);
+ for (type = 0; type < CCS_MAX_MKDEV_OPERATION; type++)
+ if (ccs_permstr(operation,
+ ccs_mac_keywords[ccs_pnnn2mac[type]]))
+ perm |= 1 << type;
+ if (perm)
+ return ccs_update_mkdev_acl(perm, param);
+ if (ccs_permstr(operation, ccs_mac_keywords[CCS_MAC_FILE_MOUNT]))
+ return ccs_update_mount_acl(param);
+ return -EINVAL;
+}
+
+/**
+ * ccs_parse_ipaddr_union - Parse an IP address.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ * @ptr: Pointer to "struct ccs_ipaddr_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool ccs_parse_ipaddr_union(struct ccs_acl_param *param,
+ struct ccs_ipaddr_union *ptr)
+{
+ struct ccs_ip_address_entry e;
+ memset(ptr, 0, sizeof(ptr));
+ if (ccs_parse_ip(ccs_read_token(param), &e) == 0) {
+ memmove(&ptr->ip[0], e.min, sizeof(ptr->ip[0]));
+ memmove(&ptr->ip[1], e.max, sizeof(ptr->ip[1]));
+ ptr->is_ipv6 = e.is_ipv6;
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Routines for printing IPv4 or IPv6 address.
+ * These are copied from include/linux/kernel.h include/net/ipv6.h
+ * include/net/addrconf.h lib/hexdump.c lib/vsprintf.c and simplified.
+ */
+static const char hex_asc[] = "0123456789abcdef";
+#define hex_asc_lo(x) hex_asc[((x) & 0x0f)]
+#define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4]
+
+static inline char *pack_hex_byte(char *buf, u8 byte)
+{
+ *buf++ = hex_asc_hi(byte);
+ *buf++ = hex_asc_lo(byte);
+ return buf;
+}
+
+static inline int ipv6_addr_v4mapped(const struct in6_addr *a)
+{
+ return (a->s6_addr32[0] | a->s6_addr32[1] |
+ (a->s6_addr32[2] ^ htonl(0x0000ffff))) == 0;
+}
+
+static inline int ipv6_addr_is_isatap(const struct in6_addr *addr)
+{
+ return (addr->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE);
+}
+
+static char *ip4_string(char *p, const u8 *addr)
+{
+ /*
+ * Since this function is called outside vsnprintf(), I can use
+ * sprintf() here.
+ */
+ return p +
+ sprintf(p, "%u.%u.%u.%u", addr[0], addr[1], addr[2], addr[3]);
+}
+
+static char *ip6_compressed_string(char *p, const char *addr)
+{
+ int i, j, range;
+ unsigned char zerolength[8];
+ int longest = 1;
+ int colonpos = -1;
+ u16 word;
+ u8 hi, lo;
+ bool needcolon = false;
+ bool useIPv4;
+ struct in6_addr in6;
+
+ memcpy(&in6, addr, sizeof(struct in6_addr));
+
+ useIPv4 = ipv6_addr_v4mapped(&in6) || ipv6_addr_is_isatap(&in6);
+
+ memset(zerolength, 0, sizeof(zerolength));
+
+ if (useIPv4)
+ range = 6;
+ else
+ range = 8;
+
+ /* find position of longest 0 run */
+ for (i = 0; i < range; i++) {
+ for (j = i; j < range; j++) {
+ if (in6.s6_addr16[j] != 0)
+ break;
+ zerolength[i]++;
+ }
+ }
+ for (i = 0; i < range; i++) {
+ if (zerolength[i] > longest) {
+ longest = zerolength[i];
+ colonpos = i;
+ }
+ }
+ if (longest == 1) /* don't compress a single 0 */
+ colonpos = -1;
+
+ /* emit address */
+ for (i = 0; i < range; i++) {
+ if (i == colonpos) {
+ if (needcolon || i == 0)
+ *p++ = ':';
+ *p++ = ':';
+ needcolon = false;
+ i += longest - 1;
+ continue;
+ }
+ if (needcolon) {
+ *p++ = ':';
+ needcolon = false;
+ }
+ /* hex u16 without leading 0s */
+ word = ntohs(in6.s6_addr16[i]);
+ hi = word >> 8;
+ lo = word & 0xff;
+ if (hi) {
+ if (hi > 0x0f)
+ p = pack_hex_byte(p, hi);
+ else
+ *p++ = hex_asc_lo(hi);
+ p = pack_hex_byte(p, lo);
+ }
+ else if (lo > 0x0f)
+ p = pack_hex_byte(p, lo);
+ else
+ *p++ = hex_asc_lo(lo);
+ needcolon = true;
+ }
+
+ if (useIPv4) {
+ if (needcolon)
+ *p++ = ':';
+ p = ip4_string(p, &in6.s6_addr[12]);
+ }
+ *p = '\0';
+
+ return p;
+}
+
+/**
+ * ccs_print_ipv4 - Print an IPv4 address.
+ *
+ * @min_ip: Pointer to "u32 in network byte order".
+ * @max_ip: Pointer to "u32 in network byte order".
+ *
+ * Returns nothing.
+ */
+static void ccs_print_ipv4(const u32 *min_ip, const u32 *max_ip)
+{
+ char addr[sizeof("255.255.255.255")];
+ ip4_string(addr, (const u8 *) min_ip);
+ cprintf("%s", addr);
+ if (*min_ip == *max_ip)
+ return;
+ ip4_string(addr, (const u8 *) max_ip);
+ cprintf("-%s", addr);
+}
+
+/**
+ * ccs_print_ipv6 - Print an IPv6 address.
+ *
+ * @min_ip: Pointer to "struct in6_addr".
+ * @max_ip: Pointer to "struct in6_addr".
+ *
+ * Returns nothing.
+ */
+static void ccs_print_ipv6(const struct in6_addr *min_ip,
+ const struct in6_addr *max_ip)
+{
+ char addr[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")];
+ ip6_compressed_string(addr, (const char *) min_ip);
+ cprintf("%s", addr);
+ if (!memcmp(min_ip, max_ip, 16))
+ return;
+ ip6_compressed_string(addr, (const char *) max_ip);
+ cprintf("-%s", addr);
+}
+
+/**
+ * ccs_print_ip - Print an IP address.
+ *
+ * @ptr: Pointer to "struct ccs_ipaddr_union".
+ *
+ * Returns nothing.
+ */
+static void ccs_print_ip(const struct ccs_ipaddr_union *ptr)
+{
+ if (ptr->is_ipv6)
+ ccs_print_ipv6(&ptr->ip[0], &ptr->ip[1]);
+ else
+ ccs_print_ipv4(&ptr->ip[0].s6_addr32[0],
+ &ptr->ip[1].s6_addr32[0]);
+}
+
+/**
+ * ccs_same_inet_acl - Check for duplicated "struct ccs_inet_acl" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_info".
+ * @b: Pointer to "struct ccs_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
+static bool ccs_same_inet_acl(const struct ccs_acl_info *a,
+ const struct ccs_acl_info *b)
+{
+ const struct ccs_inet_acl *p1 = container_of(a, typeof(*p1), head);
+ const struct ccs_inet_acl *p2 = container_of(b, typeof(*p2), head);
+ return p1->protocol == p2->protocol &&
+ ccs_same_ipaddr_union(&p1->address, &p2->address) &&
+ ccs_same_number_union(&p1->port, &p2->port);
+}
+
+/**
+ * ccs_same_unix_acl - Check for duplicated "struct ccs_unix_acl" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_info".
+ * @b: Pointer to "struct ccs_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
+static bool ccs_same_unix_acl(const struct ccs_acl_info *a,
+ const struct ccs_acl_info *b)
+{
+ const struct ccs_unix_acl *p1 = container_of(a, typeof(*p1), head);
+ const struct ccs_unix_acl *p2 = container_of(b, typeof(*p2), head);
+ return p1->protocol == p2->protocol &&
+ ccs_same_name_union(&p1->name, &p2->name);
+}
+
+/**
+ * ccs_merge_inet_acl - Merge duplicated "struct ccs_inet_acl" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_info".
+ * @b: Pointer to "struct ccs_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
+static bool ccs_merge_inet_acl(struct ccs_acl_info *a, struct ccs_acl_info *b,
+ const bool is_delete)
+{
+ u8 * const a_perm = &container_of(a, struct ccs_inet_acl, head)->perm;
+ u8 perm = *a_perm;
+ const u8 b_perm = container_of(b, struct ccs_inet_acl, head)->perm;
+ if (is_delete)
+ perm &= ~b_perm;
+ else
+ perm |= b_perm;
+ *a_perm = perm;
+ return !perm;
+}
+
+/**
+ * ccs_merge_unix_acl - Merge duplicated "struct ccs_unix_acl" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_info".
+ * @b: Pointer to "struct ccs_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
+static bool ccs_merge_unix_acl(struct ccs_acl_info *a, struct ccs_acl_info *b,
+ const bool is_delete)
+{
+ u8 * const a_perm = &container_of(a, struct ccs_unix_acl, head)->perm;
+ u8 perm = *a_perm;
+ const u8 b_perm = container_of(b, struct ccs_unix_acl, head)->perm;
+ if (is_delete)
+ perm &= ~b_perm;
+ else
+ perm |= b_perm;
+ *a_perm = perm;
+ return !perm;
+}
+
+/**
+ * ccs_write_inet_network - Write "struct ccs_inet_acl" list.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_write_inet_network(struct ccs_acl_param *param)
+{
+ struct ccs_inet_acl e = { .head.type = CCS_TYPE_INET_ACL };
+ int error = -EINVAL;
+ u8 type;
+ const char *protocol = ccs_read_token(param);
+ const char *operation = ccs_read_token(param);
+ for (e.protocol = 0; e.protocol < CCS_SOCK_MAX; e.protocol++)
+ if (!strcmp(protocol, ccs_proto_keyword[e.protocol]))
+ break;
+ for (type = 0; type < CCS_MAX_NETWORK_OPERATION; type++)
+ if (ccs_permstr(operation, ccs_socket_keyword[type]))
+ e.perm |= 1 << type;
+ if (e.protocol == CCS_SOCK_MAX || !e.perm)
+ return -EINVAL;
+ if (param->data[0] == '@') {
+ param->data++;
+ e.address.group = ccs_get_group(param, &ccs_address_group);
+ if (!e.address.group)
+ return -ENOMEM;
+ } else {
+ if (!ccs_parse_ipaddr_union(param, &e.address))
+ goto out;
+ }
+ if (!ccs_parse_number_union(param, &e.port) ||
+ e.port.values[1] > 65535)
+ goto out;
+ error = ccs_update_domain(&e.head, sizeof(e), param, ccs_same_inet_acl,
+ ccs_merge_inet_acl);
+out:
+ ccs_put_group(e.address.group);
+ ccs_put_number_union(&e.port);
+ return error;
+}
+
+/**
+ * ccs_write_unix_network - Write "struct ccs_unix_acl" list.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_write_unix_network(struct ccs_acl_param *param)
+{
+ struct ccs_unix_acl e = { .head.type = CCS_TYPE_UNIX_ACL };
+ int error;
+ u8 type;
+ const char *protocol = ccs_read_token(param);
+ const char *operation = ccs_read_token(param);
+ for (e.protocol = 0; e.protocol < CCS_SOCK_MAX; e.protocol++)
+ if (!strcmp(protocol, ccs_proto_keyword[e.protocol]))
+ break;
+ for (type = 0; type < CCS_MAX_NETWORK_OPERATION; type++)
+ if (ccs_permstr(operation, ccs_socket_keyword[type]))
+ e.perm |= 1 << type;
+ if (e.protocol == CCS_SOCK_MAX || !e.perm)
+ return -EINVAL;
+ if (!ccs_parse_name_union(param, &e.name))
+ return -EINVAL;
+ error = ccs_update_domain(&e.head, sizeof(e), param, ccs_same_unix_acl,
+ ccs_merge_unix_acl);
+ ccs_put_name_union(&e.name);
+ return error;
+}
+
+/**
+ * ccs_same_capability_acl - Check for duplicated "struct ccs_capability_acl" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_info".
+ * @b: Pointer to "struct ccs_acl_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool ccs_same_capability_acl(const struct ccs_acl_info *a,
+ const struct ccs_acl_info *b)
+{
+ const struct ccs_capability_acl *p1 = container_of(a, typeof(*p1),
+ head);
+ const struct ccs_capability_acl *p2 = container_of(b, typeof(*p2),
+ head);
+ return p1->operation == p2->operation;
+}
+
+/**
+ * ccs_write_capability - Write "struct ccs_capability_acl" list.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_write_capability(struct ccs_acl_param *param)
+{
+ struct ccs_capability_acl e = { .head.type = CCS_TYPE_CAPABILITY_ACL };
+ const char *operation = ccs_read_token(param);
+ for (e.operation = 0; e.operation < CCS_MAX_CAPABILITY_INDEX;
+ e.operation++) {
+ if (strcmp(operation,
+ ccs_mac_keywords[ccs_c2mac[e.operation]]))
+ continue;
+ return ccs_update_domain(&e.head, sizeof(e), param,
+ ccs_same_capability_acl, NULL);
+ }
+ return -EINVAL;
+}
+
+/**
+ * ccs_same_env_acl - Check for duplicated "struct ccs_env_acl" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_info".
+ * @b: Pointer to "struct ccs_acl_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool ccs_same_env_acl(const struct ccs_acl_info *a,
+ const struct ccs_acl_info *b)
+{
+ const struct ccs_env_acl *p1 = container_of(a, typeof(*p1), head);
+ const struct ccs_env_acl *p2 = container_of(b, typeof(*p2), head);
+ return p1->env == p2->env;
+}
+
+/**
+ * ccs_write_env - Write "struct ccs_env_acl" list.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_write_env(struct ccs_acl_param *param)
+{
+ struct ccs_env_acl e = { .head.type = CCS_TYPE_ENV_ACL };
+ int error = -ENOMEM;
+ const char *data = ccs_read_token(param);
+ if (!ccs_correct_word(data) || strchr(data, '='))
+ return -EINVAL;
+ e.env = ccs_get_name(data);
+ error = ccs_update_domain(&e.head, sizeof(e), param,
+ ccs_same_env_acl, NULL);
+ ccs_put_name(e.env);
+ return error;
+}
+
+/**
+ * ccs_write_misc - Update environment variable list.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_write_misc(struct ccs_acl_param *param)
+{
+ if (ccs_str_starts(param->data, "env "))
+ return ccs_write_env(param);
+ return -EINVAL;
+}
+
+/**
+ * ccs_same_signal_acl - Check for duplicated "struct ccs_signal_acl" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_info".
+ * @b: Pointer to "struct ccs_acl_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool ccs_same_signal_acl(const struct ccs_acl_info *a,
+ const struct ccs_acl_info *b)
+{
+ const struct ccs_signal_acl *p1 = container_of(a, typeof(*p1), head);
+ const struct ccs_signal_acl *p2 = container_of(b, typeof(*p2), head);
+ return ccs_same_number_union(&p1->sig, &p2->sig) &&
+ p1->domainname == p2->domainname;
+}
+
+/**
+ * ccs_write_ipc - Update "struct ccs_signal_acl" list.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_write_ipc(struct ccs_acl_param *param)
+{
+ struct ccs_signal_acl e = { .head.type = CCS_TYPE_SIGNAL_ACL };
+ int error;
+ if (!ccs_parse_number_union(param, &e.sig))
+ return -EINVAL;
+ e.domainname = ccs_get_domainname(param);
+ if (!e.domainname)
+ error = -EINVAL;
+ else
+ error = ccs_update_domain(&e.head, sizeof(e), param,
+ ccs_same_signal_acl, NULL);
+ ccs_put_name(e.domainname);
+ ccs_put_number_union(&e.sig);
+ return error;
+}
+
+
+/**
+ * ccs_same_reserved - Check for duplicated "struct ccs_reserved" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_head".
+ * @b: Pointer to "struct ccs_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool ccs_same_reserved(const struct ccs_acl_head *a,
+ const struct ccs_acl_head *b)
+{
+ const struct ccs_reserved *p1 = container_of(a, typeof(*p1), head);
+ const struct ccs_reserved *p2 = container_of(b, typeof(*p2), head);
+ return p1->ns == p2->ns && ccs_same_number_union(&p1->port, &p2->port);
+}
+
+/**
+ * ccs_write_reserved_port - Update "struct ccs_reserved" list.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_write_reserved_port(struct ccs_acl_param *param)
+{
+ struct ccs_reserved e = { .ns = param->ns };
+ int error;
+ if (param->data[0] == '@' || !ccs_parse_number_union(param, &e.port) ||
+ e.port.values[1] > 65535 || param->data[0])
+ return -EINVAL;
+ param->list = &ccs_reserved_list;
+ error = ccs_update_policy(&e.head, sizeof(e), param,
+ ccs_same_reserved);
+ /*
+ * ccs_put_number_union() is not needed because param->data[0] != '@'.
+ */
+ return error;
+}
+
+/**
+ * ccs_print_namespace - Print namespace header.
+ *
+ * @ns: Pointer to "struct ccs_policy_namespace".
+ *
+ * Returns nothing.
+ */
+static void ccs_print_namespace(const struct ccs_policy_namespace *ns)
+{
+ if (!ccs_namespace_enabled)
+ return;
+ cprintf("%s ", ns->name);
+}
+
+/**
+ * ccs_assign_profile - Create a new profile.
+ *
+ * @ns: Pointer to "struct ccs_policy_namespace".
+ * @profile: Profile number to create.
+ *
+ * Returns pointer to "struct ccs_profile" on success, NULL otherwise.
+ */
+static struct ccs_profile *ccs_assign_profile(struct ccs_policy_namespace *ns,
+ const unsigned int profile)
+{
+ struct ccs_profile *ptr;
+ if (profile >= CCS_MAX_PROFILES)
+ return NULL;
+ ptr = ns->profile_ptr[profile];
+ if (ptr)
+ return ptr;
+ ptr = ccs_malloc(sizeof(*ptr));
+ ptr->default_config = CCS_CONFIG_DISABLED |
+ CCS_CONFIG_WANT_GRANT_LOG | CCS_CONFIG_WANT_REJECT_LOG;
+ memset(ptr->config, CCS_CONFIG_USE_DEFAULT,
+ sizeof(ptr->config));
+ ptr->pref[CCS_PREF_MAX_AUDIT_LOG] = 1024;
+ ptr->pref[CCS_PREF_MAX_LEARNING_ENTRY] = 2048;
+ ns->profile_ptr[profile] = ptr;
+ return ptr;
+}
+
+/**
+ * ccs_find_yesno - Find values for specified keyword.
+ *
+ * @string: String to check.
+ * @find: Name of keyword.
+ *
+ * Returns 1 if "@find=yes" was found, 0 if "@find=no" was found, -1 otherwise.
+ */
+static s8 ccs_find_yesno(const char *string, const char *find)
+{
+ const char *cp = strstr(string, find);
+ if (cp) {
+ cp += strlen(find);
+ if (!strncmp(cp, "=yes", 4))
+ return 1;
+ else if (!strncmp(cp, "=no", 3))
+ return 0;
+ }
+ return -1;
+}
+
+/**
+ * ccs_set_uint - Set value for specified preference.
+ *
+ * @i: Pointer to "unsigned int".
+ * @string: String to check.
+ * @find: Name of keyword.
+ *
+ * Returns nothing.
+ */
+static void ccs_set_uint(unsigned int *i, const char *string, const char *find)
+{
+ const char *cp = strstr(string, find);
+ if (cp)
+ sscanf(cp + strlen(find), "=%u", i);
+}
+
+/**
+ * ccs_set_mode - Set mode for specified profile.
+ *
+ * @name: Name of functionality.
+ * @value: Mode for @name.
+ * @profile: Pointer to "struct ccs_profile".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_set_mode(char *name, const char *value,
+ struct ccs_profile *profile)
+{
+ u8 i;
+ u8 config;
+ if (!strcmp(name, "CONFIG")) {
+ i = CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX;
+ config = profile->default_config;
+ } else if (ccs_str_starts(name, "CONFIG::")) {
+ config = 0;
+ for (i = 0; i < CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX;
+ i++) {
+ int len = 0;
+ if (i < CCS_MAX_MAC_INDEX) {
+ const u8 c = ccs_index2category[i];
+ const char *category =
+ ccs_category_keywords[c];
+ len = strlen(category);
+ if (strncmp(name, category, len) ||
+ name[len++] != ':' || name[len++] != ':')
+ continue;
+ }
+ if (strcmp(name + len, ccs_mac_keywords[i]))
+ continue;
+ config = profile->config[i];
+ break;
+ }
+ if (i == CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX)
+ return -EINVAL;
+ } else {
+ return -EINVAL;
+ }
+ if (strstr(value, "use_default")) {
+ config = CCS_CONFIG_USE_DEFAULT;
+ } else {
+ u8 mode;
+ for (mode = 0; mode < CCS_CONFIG_MAX_MODE; mode++)
+ if (strstr(value, ccs_mode[mode]))
+ /*
+ * Update lower 3 bits in order to distinguish
+ * 'config' from 'CCS_CONFIG_USE_DEAFULT'.
+ */
+ config = (config & ~7) | mode;
+ if (config != CCS_CONFIG_USE_DEFAULT) {
+ switch (ccs_find_yesno(value, "grant_log")) {
+ case 1:
+ config |= CCS_CONFIG_WANT_GRANT_LOG;
+ break;
+ case 0:
+ config &= ~CCS_CONFIG_WANT_GRANT_LOG;
+ break;
+ }
+ switch (ccs_find_yesno(value, "reject_log")) {
+ case 1:
+ config |= CCS_CONFIG_WANT_REJECT_LOG;
+ break;
+ case 0:
+ config &= ~CCS_CONFIG_WANT_REJECT_LOG;
+ break;
+ }
+ }
+ }
+ if (i < CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX)
+ profile->config[i] = config;
+ else if (config != CCS_CONFIG_USE_DEFAULT)
+ profile->default_config = config;
+ return 0;
+}
+
+/**
+ * ccs_write_profile - Write profile table.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_write_profile(void)
+{
+ char *data = head.data;
+ unsigned int i;
+ char *cp;
+ struct ccs_profile *profile;
+ if (sscanf(data, "PROFILE_VERSION=%u", &head.ns->profile_version)
+ == 1)
+ return 0;
+ i = strtoul(data, &cp, 10);
+ if (*cp != '-')
+ return -EINVAL;
+ data = cp + 1;
+ profile = ccs_assign_profile(head.ns, i);
+ if (!profile)
+ return -EINVAL;
+ cp = strchr(data, '=');
+ if (!cp)
+ return -EINVAL;
+ *cp++ = '\0';
+ if (!strcmp(data, "COMMENT")) {
+ const struct ccs_path_info *new_comment = ccs_get_name(cp);
+ const struct ccs_path_info *old_comment = profile->comment;
+ profile->comment = new_comment;
+ ccs_put_name(old_comment);
+ return 0;
+ }
+ if (!strcmp(data, "PREFERENCE")) {
+ for (i = 0; i < CCS_MAX_PREF; i++)
+ ccs_set_uint(&profile->pref[i], cp,
+ ccs_pref_keywords[i]);
+ return 0;
+ }
+ return ccs_set_mode(data, cp, profile);
+}
+
+/**
+ * ccs_print_config - Print mode for specified functionality.
+ *
+ * @config: Mode for that functionality.
+ *
+ * Returns nothing.
+ *
+ * Caller prints functionality's name.
+ */
+static void ccs_print_config(const u8 config)
+{
+ cprintf("={ mode=%s grant_log=%s reject_log=%s }\n",
+ ccs_mode[config & 3],
+ ccs_yesno(config & CCS_CONFIG_WANT_GRANT_LOG),
+ ccs_yesno(config & CCS_CONFIG_WANT_REJECT_LOG));
+}
+
+/**
+ * ccs_read_profile - Read profile table.
+ *
+ * Returns nothing.
+ */
+static void ccs_read_profile(void)
+{
+ struct ccs_policy_namespace *ns;
+ if (head.eof)
+ return;
+ list_for_each_entry(ns, &ccs_namespace_list, namespace_list) {
+ u16 index;
+ ccs_print_namespace(ns);
+ cprintf("PROFILE_VERSION=%u\n", ns->profile_version);
+ for (index = 0; index < CCS_MAX_PROFILES; index++) {
+ u8 i;
+ const struct ccs_profile *profile =
+ ns->profile_ptr[index];
+ if (!profile)
+ continue;
+ ccs_print_namespace(ns);
+ cprintf("%u-COMMENT=%s\n", index, profile->comment ?
+ profile->comment->name : "");
+ ccs_print_namespace(ns);
+ cprintf("%u-PREFERENCE={ ", index);
+ for (i = 0; i < CCS_MAX_PREF; i++)
+ cprintf("%s=%u ", ccs_pref_keywords[i],
+ profile->pref[i]);
+ cprintf("}\n");
+ ccs_print_namespace(ns);
+ cprintf("%u-CONFIG", index);
+ ccs_print_config(profile->default_config);
+ for (i = 0; i < CCS_MAX_MAC_INDEX
+ + CCS_MAX_MAC_CATEGORY_INDEX; i++) {
+ const u8 config = profile->config[i];
+ if (config == CCS_CONFIG_USE_DEFAULT)
+ continue;
+ ccs_print_namespace(ns);
+ if (i < CCS_MAX_MAC_INDEX)
+ cprintf("%u-CONFIG::%s::%s", index,
+ ccs_category_keywords
+ [ccs_index2category[i]],
+ ccs_mac_keywords[i]);
+ else
+ cprintf("%u-CONFIG::%s", index,
+ ccs_mac_keywords[i]);
+ ccs_print_config(config);
+ }
+ }
+ }
+ head.eof = true;
+}
+
+/**
+ * ccs_same_manager - Check for duplicated "struct ccs_manager" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_head".
+ * @b: Pointer to "struct ccs_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool ccs_same_manager(const struct ccs_acl_head *a,
+ const struct ccs_acl_head *b)
+{
+ return container_of(a, struct ccs_manager, head)->manager
+ == container_of(b, struct ccs_manager, head)->manager;
+}
+
+/**
+ * ccs_update_manager_entry - Add a manager entry.
+ *
+ * @manager: The path to manager or the domainnamme.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static inline int ccs_update_manager_entry(const char *manager,
+ const bool is_delete)
+{
+ struct ccs_manager e = { };
+ struct ccs_acl_param param = {
+ .is_delete = is_delete,
+ .list = &ccs_manager_list,
+ };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (ccs_domain_def(manager)) {
+ if (!ccs_correct_domain(manager))
+ return -EINVAL;
+ e.is_domain = true;
+ } else {
+ if (!ccs_correct_path(manager))
+ return -EINVAL;
+ }
+ e.manager = ccs_get_name(manager);
+ error = ccs_update_policy(&e.head, sizeof(e), ¶m,
+ ccs_same_manager);
+ ccs_put_name(e.manager);
+ return error;
+}
+
+/**
+ * ccs_write_manager - Write manager policy.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_write_manager(void)
+{
+ return ccs_update_manager_entry(head.data, head.is_delete);
+}
+
+/**
+ * ccs_read_manager - Read manager policy.
+ *
+ * Returns nothing.
+ */
+static void ccs_read_manager(void)
+{
+ struct ccs_manager *ptr;
+ if (head.eof)
+ return;
+ list_for_each_entry(ptr, &ccs_manager_list, head.list)
+ if (!ptr->head.is_deleted)
+ cprintf("%s\n", ptr->manager->name);
+ head.eof = true;
+}
+
+/**
+ * ccs_select_domain - Parse select command.
+ *
+ * @data: String to parse.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool ccs_select_domain(const char *data)
+{
+ struct ccs_domain2_info *domain = NULL;
+ if (strncmp(data, "select ", 7))
+ return false;
+ data += 7;
+ if (!strncmp(data, "domain=", 7)) {
+ if (*(data + 7) == '<')
+ domain = ccs_find_domain2(data + 7);
+ } else
+ return false;
+ if (domain) {
+ head.domain = domain;
+ head.print_this_domain_only = domain;
+ } else
+ head.eof = true;
+ cprintf("# select %s\n", data);
+ return true;
+}
+
+/**
+ * ccs_same_handler_acl - Check for duplicated "struct ccs_handler_acl" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_info".
+ * @b: Pointer to "struct ccs_acl_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool ccs_same_handler_acl(const struct ccs_acl_info *a,
+ const struct ccs_acl_info *b)
+{
+ const struct ccs_handler_acl *p1 = container_of(a, typeof(*p1), head);
+ const struct ccs_handler_acl *p2 = container_of(b, typeof(*p2), head);
+ return p1->handler == p2->handler;
+}
+
+/**
+ * ccs_same_task_acl - Check for duplicated "struct ccs_task_acl" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_info".
+ * @b: Pointer to "struct ccs_acl_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool ccs_same_task_acl(const struct ccs_acl_info *a,
+ const struct ccs_acl_info *b)
+{
+ const struct ccs_task_acl *p1 = container_of(a, typeof(*p1), head);
+ const struct ccs_task_acl *p2 = container_of(b, typeof(*p2), head);
+ return p1->domainname == p2->domainname;
+}
+
+/**
+ * ccs_write_task - Update task related list.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_write_task(struct ccs_acl_param *param)
+{
+ int error;
+ const bool is_auto = ccs_str_starts(param->data,
+ "auto_domain_transition ");
+ if (!is_auto && !ccs_str_starts(param->data,
+ "manual_domain_transition ")) {
+ struct ccs_handler_acl e = { };
+ char *handler;
+ if (ccs_str_starts(param->data, "auto_execute_handler "))
+ e.head.type = CCS_TYPE_AUTO_EXECUTE_HANDLER;
+ else if (ccs_str_starts(param->data,
+ "denied_execute_handler "))
+ e.head.type = CCS_TYPE_DENIED_EXECUTE_HANDLER;
+ else
+ return -EINVAL;
+ handler = ccs_read_token(param);
+ if (!ccs_correct_path(handler))
+ return -EINVAL;
+ e.handler = ccs_get_name(handler);
+ if (e.handler->is_patterned)
+ error = -EINVAL; /* No patterns allowed. */
+ else
+ error = ccs_update_domain(&e.head, sizeof(e), param,
+ ccs_same_handler_acl, NULL);
+ ccs_put_name(e.handler);
+ } else {
+ struct ccs_task_acl e = {
+ .head.type = is_auto ?
+ CCS_TYPE_AUTO_TASK_ACL : CCS_TYPE_MANUAL_TASK_ACL,
+ .domainname = ccs_get_domainname(param),
+ };
+ if (!e.domainname)
+ error = -EINVAL;
+ else
+ error = ccs_update_domain(&e.head, sizeof(e), param,
+ ccs_same_task_acl, NULL);
+ ccs_put_name(e.domainname);
+ }
+ return error;
+}
+
+/**
+ * ccs_write_domain2 - Write domain policy.
+ *
+ * @ns: Pointer to "struct ccs_policy_namespace".
+ * @list: Pointer to "struct list_head".
+ * @data: Policy to be interpreted.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_write_domain2(struct ccs_policy_namespace *ns,
+ struct list_head *list, char *data,
+ const bool is_delete)
+{
+ struct ccs_acl_param param = {
+ .ns = ns,
+ .list = list,
+ .data = data,
+ .is_delete = is_delete,
+ };
+ static const struct {
+ const char *keyword;
+ int (*write) (struct ccs_acl_param *);
+ } ccs_callback[7] = {
+ { "file ", ccs_write_file },
+ { "network inet ", ccs_write_inet_network },
+ { "network unix ", ccs_write_unix_network },
+ { "misc ", ccs_write_misc },
+ { "capability ", ccs_write_capability },
+ { "ipc signal ", ccs_write_ipc },
+ { "task ", ccs_write_task },
+ };
+ u8 i;
+ for (i = 0; i < 7; i++) {
+ if (!ccs_str_starts(param.data, ccs_callback[i].keyword))
+ continue;
+ return ccs_callback[i].write(¶m);
+ }
+ return -EINVAL;
+}
+
+/**
+ * ccs_delete_domain2 - Delete a domain from domain policy.
+ *
+ * @domainname: Name of domain.
+ *
+ * Returns nothing.
+ */
+static void ccs_delete_domain2(const char *domainname)
+{
+ struct ccs_domain2_info *domain;
+ list_for_each_entry(domain, &ccs_domain_list, list) {
+ if (domain == &ccs_kernel_domain)
+ continue;
+ if (strcmp(domain->domainname->name, domainname))
+ continue;
+ domain->is_deleted = true;
+ }
+}
+
+/**
+ * ccs_write_domain - Write domain policy.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_write_domain(void)
+{
+ char *data = head.data;
+ struct ccs_policy_namespace *ns;
+ struct ccs_domain2_info *domain = head.domain;
+ const bool is_delete = head.is_delete;
+ const bool is_select = !is_delete && ccs_str_starts(data, "select ");
+ unsigned int profile;
+ if (*data == '<') {
+ domain = NULL;
+ if (is_delete)
+ ccs_delete_domain2(data);
+ else if (is_select)
+ domain = ccs_find_domain2(data);
+ else
+ domain = ccs_assign_domain2(data);
+ head.domain = domain;
+ return 0;
+ }
+ if (!domain)
+ return -EINVAL;
+ ns = ccs_assign_namespace(domain->domainname->name);
+ if (!ns)
+ return -EINVAL;
+ if (sscanf(data, "use_profile %u\n", &profile) == 1
+ && profile < CCS_MAX_PROFILES) {
+ if (ns->profile_ptr[(u8) profile])
+ if (!is_delete)
+ domain->profile = (u8) profile;
+ return 0;
+ }
+ if (sscanf(data, "use_group %u\n", &profile) == 1
+ && profile < CCS_MAX_ACL_GROUPS) {
+ if (!is_delete)
+ domain->group = (u8) profile;
+ return 0;
+ }
+ for (profile = 0; profile < CCS_MAX_DOMAIN_INFO_FLAGS; profile++) {
+ const char *cp = ccs_dif[profile];
+ if (strncmp(data, cp, strlen(cp) - 1))
+ continue;
+ domain->flags[profile] = !is_delete;
+ return 0;
+ }
+ return ccs_write_domain2(ns, &domain->acl_info_list, data, is_delete);
+}
/**
- * ccs_handle_misc_policy - Handle policy data other than domain policy.
+ * ccs_print_name_union - Print a ccs_name_union.
*
- * @mp: Pointer to "struct ccs_misc_policy".
- * @fp: Pointer to "FILE".
- * @is_write: True if write request, false otherwise.
+ * @ptr: Pointer to "struct ccs_name_union".
*
* Returns nothing.
*/
-static void ccs_handle_misc_policy(struct ccs_misc_policy *mp, FILE *fp,
- _Bool is_write)
+static void ccs_print_name_union(const struct ccs_name_union *ptr)
{
- int i;
- if (!is_write)
- goto read_policy;
- while (true) {
- char *line = ccs_freadline_unpack(fp);
- const struct ccs_path_info *cp;
- _Bool is_delete;
- if (!line)
- break;
- if (!line[0])
- continue;
- is_delete = ccs_str_starts(line, "delete ");
- cp = ccs_savename(line);
- if (!cp)
- ccs_out_of_memory();
- if (!is_delete)
- goto append_policy;
- for (i = 0; i < mp->list_len; i++)
- /* Faster comparison, for they are ccs_savename'd. */
- if (mp->list[i] == cp)
+ if (ptr->group)
+ cprintf(" @%s", ptr->group->group_name->name);
+ else
+ cprintf(" %s", ptr->filename->name);
+}
+
+/**
+ * ccs_print_name_union_quoted - Print a ccs_name_union with a quote.
+ *
+ * @ptr: Pointer to "struct ccs_name_union".
+ *
+ * Returns nothing.
+ */
+static void ccs_print_name_union_quoted(const struct ccs_name_union *ptr)
+{
+ if (ptr->group)
+ cprintf("@%s", ptr->group->group_name->name);
+ else
+ cprintf("\"%s\"", ptr->filename->name);
+}
+
+/**
+ * ccs_print_number_union_nospace - Print a ccs_number_union without a space.
+ *
+ * @ptr: Pointer to "struct ccs_number_union".
+ *
+ * Returns nothing.
+ */
+static void ccs_print_number_union_nospace(const struct ccs_number_union *ptr)
+{
+ if (ptr->group) {
+ cprintf("@%s", ptr->group->group_name->name);
+ } else {
+ int i;
+ unsigned long min = ptr->values[0];
+ const unsigned long max = ptr->values[1];
+ u8 min_type = ptr->value_type[0];
+ const u8 max_type = ptr->value_type[1];
+ for (i = 0; i < 2; i++) {
+ switch (min_type) {
+ case CCS_VALUE_TYPE_HEXADECIMAL:
+ cprintf("0x%lX", min);
break;
- if (i < mp->list_len)
- for (mp->list_len--; i < mp->list_len; i++)
- mp->list[i] = mp->list[i + 1];
- continue;
-append_policy:
- for (i = 0; i < mp->list_len; i++)
- /* Faster comparison, for they are ccs_savename'd. */
- if (mp->list[i] == cp)
+ case CCS_VALUE_TYPE_OCTAL:
+ cprintf("0%lo", min);
break;
- if (i < mp->list_len)
- continue;
- mp->list = realloc(mp->list, (mp->list_len + 1)
- * sizeof(const struct ccs_path_info *));
- if (!mp->list)
- ccs_out_of_memory();
- mp->list[mp->list_len++] = cp;
+ default:
+ cprintf("%lu", min);
+ break;
+ }
+ if (min == max && min_type == max_type)
+ break;
+ cprintf("-");
+ min_type = max_type;
+ min = max;
+ }
}
- return;
-read_policy:
- for (i = 0; i < mp->list_len; i++)
- fprintf(fp, "%s\n", mp->list[i]->name);
}
/**
- * ccs_editpolicy_offline_daemon - Emulate /proc/ccs/ interface.
+ * ccs_print_number_union - Print a ccs_number_union.
*
- * This function does not return.
+ * @ptr: Pointer to "struct ccs_number_union".
+ *
+ * Returns nothing.
+ */
+static void ccs_print_number_union(const struct ccs_number_union *ptr)
+{
+ cprintf(" ");
+ ccs_print_number_union_nospace(ptr);
+}
+
+/**
+ * ccs_print_condition - Print condition part.
+ *
+ * @cond: Pointer to "struct ccs_condition".
+ *
+ * Returns true on success, false otherwise.
*/
-void ccs_editpolicy_offline_daemon(void)
-{
- struct ccs_misc_policy mp[3];
- static const int buffer_len = 8192;
- char *buffer = malloc(buffer_len);
- if (!buffer)
- ccs_out_of_memory();
- memset(&ccs_dp, 0, sizeof(ccs_dp));
- memset(&mp, 0, sizeof(mp));
- ccs_get();
- ccs_assign_domain(&ccs_dp, CCS_ROOT_NAME, false, false);
- while (true) {
- FILE *fp;
- struct msghdr msg;
- struct iovec iov = { buffer, buffer_len - 1 };
- char cmsg_buf[CMSG_SPACE(sizeof(int))];
- struct cmsghdr *cmsg = (struct cmsghdr *) cmsg_buf;
- memset(&msg, 0, sizeof(msg));
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_control = cmsg_buf;
- msg.msg_controllen = sizeof(cmsg_buf);
- memset(buffer, 0, buffer_len);
- errno = 0;
- if (recvmsg(ccs_persistent_fd, &msg, 0) <= 0)
+static void ccs_print_condition(const struct ccs_condition *cond)
+{
+ const u16 condc = cond->condc;
+ const struct ccs_condition_element *condp = (typeof(condp)) (cond + 1);
+ const struct ccs_number_union *numbers_p =
+ (typeof(numbers_p)) (condp + condc);
+ const struct ccs_name_union *names_p =
+ (typeof(names_p)) (numbers_p + cond->numbers_count);
+ const struct ccs_argv *argv =
+ (typeof(argv)) (names_p + cond->names_count);
+ const struct ccs_envp *envp = (typeof(envp)) (argv + cond->argc);
+ u16 i;
+ for (i = 0; i < condc; i++) {
+ const u8 match = condp->equals;
+ const u8 left = condp->left;
+ const u8 right = condp->right;
+ condp++;
+ cprintf(" ");
+ switch (left) {
+ case CCS_ARGV_ENTRY:
+ cprintf("exec.argv[%lu]%s=\"%s\"", argv->index,
+ argv->is_not ? "!" : "", argv->value->name);
+ argv++;
+ continue;
+ case CCS_ENVP_ENTRY:
+ cprintf("exec.envp[\"%s\"]%s=",
+ envp->name->name, envp->is_not ? "!" : "");
+ if (envp->value)
+ cprintf("\"%s\"", envp->value->name);
+ else
+ cprintf("NULL");
+ envp++;
+ continue;
+ case CCS_NUMBER_UNION:
+ ccs_print_number_union_nospace(numbers_p++);
break;
- cmsg = CMSG_FIRSTHDR(&msg);
- if (!cmsg)
+ default:
+ cprintf("%s", ccs_condition_keyword[left]);
break;
- if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_RIGHTS &&
- cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
- const int *fdp = (int *) CMSG_DATA(cmsg);
- const int fd = *fdp;
- fp = fdopen(fd, "w+");
- if (!fp) {
- close(fd);
+ }
+ cprintf("%s", match ? "=" : "!=");
+ switch (right) {
+ case CCS_NAME_UNION:
+ ccs_print_name_union_quoted(names_p++);
+ break;
+ case CCS_NUMBER_UNION:
+ ccs_print_number_union_nospace(numbers_p++);
+ break;
+ default:
+ cprintf("%s", ccs_condition_keyword[right]);
+ break;
+ }
+ }
+ if (cond->grant_log != CCS_GRANTLOG_AUTO)
+ cprintf(" grant_log=%s",
+ ccs_yesno(cond->grant_log == CCS_GRANTLOG_YES));
+ if (cond->transit)
+ cprintf(" auto_domain_transition=\"%s\"",
+ cond->transit->name);
+}
+
+/**
+ * ccs_set_group - Print "acl_group " header keyword and category name.
+ *
+ * @category: Category name.
+ *
+ * Returns nothing.
+ */
+static void ccs_set_group(const char *category)
+{
+ if (head.type == CCS_EXCEPTIONPOLICY) {
+ ccs_print_namespace(head.ns);
+ cprintf("acl_group %u ", head.acl_group_index);
+ }
+ cprintf("%s", category);
+}
+
+/**
+ * ccs_print_entry - Print an ACL entry.
+ *
+ * @acl: Pointer to an ACL entry.
+ *
+ * Returns nothing.
+ */
+static void ccs_print_entry(const struct ccs_acl_info *acl)
+{
+ const u8 acl_type = acl->type;
+ const bool may_trigger_transition = acl->cond && acl->cond->transit;
+ bool first = true;
+ u8 bit;
+ if (acl->is_deleted)
+ return;
+ if (acl_type == CCS_TYPE_PATH_ACL) {
+ struct ccs_path_acl *ptr
+ = container_of(acl, typeof(*ptr), head);
+ const u16 perm = ptr->perm;
+ for (bit = 0; bit < CCS_MAX_PATH_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ if (head.print_transition_related_only &&
+ bit != CCS_TYPE_EXECUTE && !may_trigger_transition)
continue;
+ if (first) {
+ ccs_set_group("file ");
+ first = false;
+ } else {
+ cprintf("/");
}
- } else {
- break;
+ cprintf("%s", ccs_path_keyword[bit]);
}
- if (ccs_str_starts(buffer, "POST ")) {
- if (!strcmp(buffer, CCS_PROC_POLICY_DOMAIN_POLICY))
- ccs_handle_domain_policy(&ccs_dp, fp, true);
- else if (!strcmp(buffer,
- CCS_PROC_POLICY_EXCEPTION_POLICY))
- ccs_handle_misc_policy(&mp[0], fp, true);
- else if (!strcmp(buffer, CCS_PROC_POLICY_PROFILE))
- ccs_handle_misc_policy(&mp[1], fp, true);
- else if (!strcmp(buffer, CCS_PROC_POLICY_MANAGER))
- ccs_handle_misc_policy(&mp[2], fp, true);
- } else if (ccs_str_starts(buffer, "GET ")) {
- if (!strcmp(buffer, CCS_PROC_POLICY_DOMAIN_POLICY))
- ccs_handle_domain_policy(&ccs_dp, fp, false);
- else if (!strcmp(buffer,
- CCS_PROC_POLICY_EXCEPTION_POLICY))
- ccs_handle_misc_policy(&mp[0], fp, false);
- else if (!strcmp(buffer, CCS_PROC_POLICY_PROFILE))
- ccs_handle_misc_policy(&mp[1], fp, false);
- else if (!strcmp(buffer, CCS_PROC_POLICY_MANAGER))
- ccs_handle_misc_policy(&mp[2], fp, false);
- }
- fclose(fp);
- }
- ccs_put();
- ccs_clear_domain_policy(&ccs_dp);
- {
- int i;
- for (i = 0; i < 3; i++) {
- free(mp[i].list);
- mp[i].list = NULL;
- mp[i].list_len = 0;
+ if (first)
+ return;
+ ccs_print_name_union(&ptr->name);
+ } else if (acl_type == CCS_TYPE_AUTO_EXECUTE_HANDLER ||
+ acl_type == CCS_TYPE_DENIED_EXECUTE_HANDLER) {
+ struct ccs_handler_acl *ptr
+ = container_of(acl, typeof(*ptr), head);
+ ccs_set_group("task ");
+ cprintf(acl_type == CCS_TYPE_AUTO_EXECUTE_HANDLER ?
+ "auto_execute_handler " : "denied_execute_handler ");
+ cprintf("%s", ptr->handler->name);
+ } else if (acl_type == CCS_TYPE_AUTO_TASK_ACL ||
+ acl_type == CCS_TYPE_MANUAL_TASK_ACL) {
+ struct ccs_task_acl *ptr =
+ container_of(acl, typeof(*ptr), head);
+ ccs_set_group("task ");
+ cprintf(acl_type == CCS_TYPE_AUTO_TASK_ACL ?
+ "auto_domain_transition " :
+ "manual_domain_transition ");
+ cprintf("%s", ptr->domainname->name);
+ } else if (head.print_transition_related_only &&
+ !may_trigger_transition) {
+ return;
+ } else if (acl_type == CCS_TYPE_MKDEV_ACL) {
+ struct ccs_mkdev_acl *ptr =
+ container_of(acl, typeof(*ptr), head);
+ const u8 perm = ptr->perm;
+ for (bit = 0; bit < CCS_MAX_MKDEV_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ if (first) {
+ ccs_set_group("file ");
+ first = false;
+ } else {
+ cprintf("/");
+ }
+ cprintf("%s", ccs_mac_keywords[ccs_pnnn2mac[bit]]);
+ }
+ if (first)
+ return;
+ ccs_print_name_union(&ptr->name);
+ ccs_print_number_union(&ptr->mode);
+ ccs_print_number_union(&ptr->major);
+ ccs_print_number_union(&ptr->minor);
+ } else if (acl_type == CCS_TYPE_PATH2_ACL) {
+ struct ccs_path2_acl *ptr =
+ container_of(acl, typeof(*ptr), head);
+ const u8 perm = ptr->perm;
+ for (bit = 0; bit < CCS_MAX_PATH2_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ if (first) {
+ ccs_set_group("file ");
+ first = false;
+ } else {
+ cprintf("/");
+ }
+ cprintf("%s", ccs_mac_keywords[ccs_pp2mac[bit]]);
+ }
+ if (first)
+ return;
+ ccs_print_name_union(&ptr->name1);
+ ccs_print_name_union(&ptr->name2);
+ } else if (acl_type == CCS_TYPE_PATH_NUMBER_ACL) {
+ struct ccs_path_number_acl *ptr =
+ container_of(acl, typeof(*ptr), head);
+ const u8 perm = ptr->perm;
+ for (bit = 0; bit < CCS_MAX_PATH_NUMBER_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ if (first) {
+ ccs_set_group("file ");
+ first = false;
+ } else {
+ cprintf("/");
+ }
+ cprintf("%s", ccs_mac_keywords[ccs_pn2mac[bit]]);
+ }
+ if (first)
+ return;
+ ccs_print_name_union(&ptr->name);
+ ccs_print_number_union(&ptr->number);
+ } else if (acl_type == CCS_TYPE_ENV_ACL) {
+ struct ccs_env_acl *ptr =
+ container_of(acl, typeof(*ptr), head);
+ ccs_set_group("misc env ");
+ cprintf("%s", ptr->env->name);
+ } else if (acl_type == CCS_TYPE_CAPABILITY_ACL) {
+ struct ccs_capability_acl *ptr =
+ container_of(acl, typeof(*ptr), head);
+ ccs_set_group("capability ");
+ cprintf("%s", ccs_mac_keywords[ccs_c2mac[ptr->operation]]);
+ } else if (acl_type == CCS_TYPE_INET_ACL) {
+ struct ccs_inet_acl *ptr =
+ container_of(acl, typeof(*ptr), head);
+ const u8 perm = ptr->perm;
+ for (bit = 0; bit < CCS_MAX_NETWORK_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ if (first) {
+ ccs_set_group("network inet ");
+ cprintf("%s ",
+ ccs_proto_keyword[ptr->protocol]);
+ first = false;
+ } else {
+ cprintf("/");
+ }
+ cprintf("%s", ccs_socket_keyword[bit]);
+ }
+ if (first)
+ return;
+ cprintf(" ");
+ if (ptr->address.group)
+ cprintf("@%s", ptr->address.group->group_name->name);
+ else
+ ccs_print_ip(&ptr->address);
+ ccs_print_number_union(&ptr->port);
+ } else if (acl_type == CCS_TYPE_UNIX_ACL) {
+ struct ccs_unix_acl *ptr =
+ container_of(acl, typeof(*ptr), head);
+ const u8 perm = ptr->perm;
+ for (bit = 0; bit < CCS_MAX_NETWORK_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ if (first) {
+ ccs_set_group("network unix ");
+ cprintf("%s ",
+ ccs_proto_keyword[ptr->protocol]);
+ first = false;
+ } else {
+ cprintf("/");
+ }
+ cprintf("%s", ccs_socket_keyword[bit]);
+ }
+ if (first)
+ return;
+ ccs_print_name_union(&ptr->name);
+ } else if (acl_type == CCS_TYPE_SIGNAL_ACL) {
+ struct ccs_signal_acl *ptr =
+ container_of(acl, typeof(*ptr), head);
+ ccs_set_group("ipc signal ");
+ ccs_print_number_union_nospace(&ptr->sig);
+ cprintf(" %s", ptr->domainname->name);
+ } else if (acl_type == CCS_TYPE_MOUNT_ACL) {
+ struct ccs_mount_acl *ptr =
+ container_of(acl, typeof(*ptr), head);
+ ccs_set_group("file mount");
+ ccs_print_name_union(&ptr->dev_name);
+ ccs_print_name_union(&ptr->dir_name);
+ ccs_print_name_union(&ptr->fs_type);
+ ccs_print_number_union(&ptr->flags);
+ }
+ if (acl->cond)
+ ccs_print_condition(acl->cond);
+ cprintf("\n");
+}
+
+/**
+ * ccs_read_domain2 - Read domain policy.
+ *
+ * @list: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
+static void ccs_read_domain2(struct list_head *list)
+{
+ struct ccs_acl_info *ptr;
+ list_for_each_entry(ptr, list, list)
+ ccs_print_entry(ptr);
+}
+
+/**
+ * ccs_read_domain - Read domain policy.
+ *
+ * Returns nothing.
+ */
+static void ccs_read_domain(void)
+{
+ struct ccs_domain2_info *domain;
+ if (head.eof)
+ return;
+ list_for_each_entry(domain, &ccs_domain_list, list) {
+ u8 i;
+ if (domain->is_deleted)
+ continue;
+ if (head.print_this_domain_only &&
+ head.print_this_domain_only != domain)
+ continue;
+ /* Print domainname and flags. */
+ cprintf("%s\n", domain->domainname->name);
+ cprintf("use_profile %u\n", domain->profile);
+ cprintf("use_group %u\n", domain->group);
+ for (i = 0; i < CCS_MAX_DOMAIN_INFO_FLAGS; i++)
+ if (domain->flags[i])
+ cprintf("%s", ccs_dif[i]);
+ cprintf("\n");
+ ccs_read_domain2(&domain->acl_info_list);
+ cprintf("\n");
+ }
+ head.eof = true;
+}
+
+/**
+ * ccs_same_path_group - Check for duplicated "struct ccs_path_group" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_head".
+ * @b: Pointer to "struct ccs_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool ccs_same_path_group(const struct ccs_acl_head *a,
+ const struct ccs_acl_head *b)
+{
+ return container_of(a, struct ccs_path_group, head)->member_name ==
+ container_of(b, struct ccs_path_group, head)->member_name;
+}
+
+/**
+ * ccs_same_number_group - Check for duplicated "struct ccs_number_group" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_head".
+ * @b: Pointer to "struct ccs_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool ccs_same_number_group(const struct ccs_acl_head *a,
+ const struct ccs_acl_head *b)
+{
+ return !memcmp(&container_of(a, struct ccs_number_group, head)->number,
+ &container_of(b, struct ccs_number_group, head)->number,
+ sizeof(container_of(a, struct ccs_number_group, head)
+ ->number));
+}
+
+/**
+ * ccs_same_address_group - Check for duplicated "struct ccs_address_group" entry.
+ *
+ * @a: Pointer to "struct ccs_acl_head".
+ * @b: Pointer to "struct ccs_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool ccs_same_address_group(const struct ccs_acl_head *a,
+ const struct ccs_acl_head *b)
+{
+ const struct ccs_address_group *p1 = container_of(a, typeof(*p1),
+ head);
+ const struct ccs_address_group *p2 = container_of(b, typeof(*p2),
+ head);
+ return ccs_same_ipaddr_union(&p1->address, &p2->address);
+}
+
+/**
+ * ccs_write_group - Write "struct ccs_path_group"/"struct ccs_number_group"/"struct ccs_address_group" list.
+ *
+ * @param: Pointer to "struct ccs_acl_param".
+ * @type: Type of this group.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_write_group(struct ccs_acl_param *param, const u8 type)
+{
+ struct ccs_group *group = ccs_get_group(param, type == CCS_PATH_GROUP ?
+ &ccs_path_group :
+ type == CCS_NUMBER_GROUP ?
+ &ccs_number_group :
+ &ccs_address_group);
+ int error = -EINVAL;
+ if (!group)
+ return -ENOMEM;
+ param->list = &group->member_list;
+ if (type == CCS_PATH_GROUP) {
+ struct ccs_path_group e = { };
+ e.member_name = ccs_get_name(ccs_read_token(param));
+ error = ccs_update_policy(&e.head, sizeof(e), param,
+ ccs_same_path_group);
+ ccs_put_name(e.member_name);
+ } else if (type == CCS_NUMBER_GROUP) {
+ struct ccs_number_group e = { };
+ if (param->data[0] == '@' ||
+ !ccs_parse_number_union(param, &e.number))
+ goto out;
+ error = ccs_update_policy(&e.head, sizeof(e), param,
+ ccs_same_number_group);
+ /*
+ * ccs_put_number_union() is not needed because
+ * param->data[0] != '@'.
+ */
+ } else {
+ struct ccs_address_group e = { };
+ if (param->data[0] == '@' ||
+ !ccs_parse_ipaddr_union(param, &e.address))
+ goto out;
+ error = ccs_update_policy(&e.head, sizeof(e), param,
+ ccs_same_address_group);
+ }
+out:
+ ccs_put_group(group);
+ return error;
+}
+
+/**
+ * ccs_write_exception - Write exception policy.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_write_exception(void)
+{
+ const bool is_delete = head.is_delete;
+ struct ccs_acl_param param = {
+ .ns = head.ns,
+ .is_delete = is_delete,
+ .data = head.data,
+ };
+ u8 i;
+ if (ccs_str_starts(param.data, "aggregator "))
+ return ccs_write_aggregator(¶m);
+ if (ccs_str_starts(param.data, "deny_autobind "))
+ return ccs_write_reserved_port(¶m);
+ for (i = 0; i < CCS_MAX_TRANSITION_TYPE; i++)
+ if (ccs_str_starts(param.data, ccs_transition_type[i]))
+ return ccs_write_transition_control(¶m, i);
+ for (i = 0; i < CCS_MAX_GROUP; i++)
+ if (ccs_str_starts(param.data, ccs_group_name[i]))
+ return ccs_write_group(¶m, i);
+ if (ccs_str_starts(param.data, "acl_group ")) {
+ unsigned int group;
+ char *data;
+ group = strtoul(param.data, &data, 10);
+ if (group < CCS_MAX_ACL_GROUPS && *data++ == ' ')
+ return ccs_write_domain2(head.ns,
+ &head.ns->acl_group[group],
+ data, is_delete);
+ }
+ return -EINVAL;
+}
+
+/**
+ * ccs_read_group - Read "struct ccs_path_group"/"struct ccs_number_group"/"struct ccs_address_group" list.
+ *
+ * @ns: Pointer to "struct ccs_policy_namespace".
+ *
+ * Returns nothing.
+ */
+static void ccs_read_group(const struct ccs_policy_namespace *ns)
+{
+ struct ccs_group *group;
+ list_for_each_entry(group, &ccs_path_group, head.list) {
+ struct ccs_acl_head *ptr;
+ list_for_each_entry(ptr, &group->member_list, list) {
+ if (group->ns != ns)
+ continue;
+ if (ptr->is_deleted)
+ continue;
+ ccs_print_namespace(group->ns);
+ cprintf("%s%s", ccs_group_name[CCS_PATH_GROUP],
+ group->group_name->name);
+ cprintf(" %s",
+ container_of(ptr, struct ccs_path_group,
+ head)->member_name->name);
+ cprintf("\n");
+ }
+ }
+ list_for_each_entry(group, &ccs_number_group, head.list) {
+ struct ccs_acl_head *ptr;
+ list_for_each_entry(ptr, &group->member_list, list) {
+ if (group->ns != ns)
+ continue;
+ if (ptr->is_deleted)
+ continue;
+ ccs_print_namespace(group->ns);
+ cprintf("%s%s", ccs_group_name[CCS_NUMBER_GROUP],
+ group->group_name->name);
+ ccs_print_number_union(&container_of
+ (ptr, struct ccs_number_group,
+ head)->number);
+ cprintf("\n");
+ }
+ }
+ list_for_each_entry(group, &ccs_address_group, head.list) {
+ struct ccs_acl_head *ptr;
+ list_for_each_entry(ptr, &group->member_list, list) {
+ if (group->ns != ns)
+ continue;
+ if (ptr->is_deleted)
+ continue;
+ ccs_print_namespace(group->ns);
+ cprintf("%s%s", ccs_group_name[CCS_ADDRESS_GROUP],
+ group->group_name->name);
+ cprintf(" ");
+ ccs_print_ip(&container_of
+ (ptr, struct ccs_address_group, head)->
+ address);
+ cprintf("\n");
+ }
+ }
+}
+
+/**
+ * ccs_read_policy - Read "struct ccs_..._entry" list.
+ *
+ * @ns: Pointer to "struct ccs_policy_namespace".
+ *
+ * Returns nothing.
+ */
+static void ccs_read_policy(const struct ccs_policy_namespace *ns)
+{
+ struct ccs_acl_head *acl;
+ if (head.print_transition_related_only)
+ goto transition_only;
+ list_for_each_entry(acl, &ccs_reserved_list, list) {
+ struct ccs_reserved *ptr =
+ container_of(acl, typeof(*ptr), head);
+ if (acl->is_deleted || ptr->ns != ns)
+ continue;
+ ccs_print_namespace(ptr->ns);
+ cprintf("deny_autobind ");
+ ccs_print_number_union_nospace(&ptr->port);
+ cprintf("\n");
+ }
+ list_for_each_entry(acl, &ccs_aggregator_list, list) {
+ struct ccs_aggregator *ptr =
+ container_of(acl, typeof(*ptr), head);
+ if (acl->is_deleted || ptr->ns != ns)
+ continue;
+ ccs_print_namespace(ptr->ns);
+ cprintf("aggregator %s %s\n", ptr->original_name->name,
+ ptr->aggregated_name->name);
+ }
+transition_only:
+ list_for_each_entry(acl, &ccs_transition_list, list) {
+ struct ccs_transition_control *ptr =
+ container_of(acl, typeof(*ptr), head);
+ if (acl->is_deleted || ptr->ns != ns)
+ continue;
+ ccs_print_namespace(ptr->ns);
+ cprintf("%s%s from %s\n", ccs_transition_type[ptr->type],
+ ptr->program ? ptr->program->name : "any",
+ ptr->domainname ? ptr->domainname->name : "any");
+ }
+}
+
+/**
+ * ccs_read_exception - Read exception policy.
+ *
+ * Returns nothing.
+ */
+static void ccs_read_exception(void)
+{
+ struct ccs_policy_namespace *ns;
+ if (head.eof)
+ return;
+ list_for_each_entry(ns, &ccs_namespace_list, namespace_list) {
+ unsigned int i;
+ head.ns = ns;
+ ccs_read_policy(ns);
+ ccs_read_group(ns);
+ for (i = 0; i < CCS_MAX_ACL_GROUPS; i++)
+ ccs_read_domain2(&ns->acl_group[i]);
+ }
+ head.eof = true;
+}
+
+/**
+ * ccs_read_stat - Read statistic data.
+ *
+ * Returns nothing.
+ */
+static void ccs_read_stat(void)
+{
+ u8 i;
+ if (head.eof)
+ return;
+ for (i = 0; i < CCS_MAX_MEMORY_STAT; i++)
+ cprintf("Memory used by %-22s %10u\n", ccs_memory_headers[i],
+ ccs_memory_quota[i]);
+ head.eof = true;
+}
+
+/**
+ * ccs_write_stat - Set memory quota.
+ *
+ * Returns 0.
+ */
+static int ccs_write_stat(void)
+{
+ char *data = head.data;
+ u8 i;
+ if (ccs_str_starts(data, "Memory used by "))
+ for (i = 0; i < CCS_MAX_MEMORY_STAT; i++)
+ if (ccs_str_starts(data, ccs_memory_headers[i]))
+ ccs_memory_quota[i] = strtoul(data, NULL, 10);
+ return 0;
+}
+
+/**
+ * ccs_parse_policy - Parse a policy line.
+ *
+ * @line: Line to parse.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int ccs_parse_policy(char *line)
+{
+ /* Delete request? */
+ head.is_delete = !strncmp(line, "delete ", 7);
+ if (head.is_delete)
+ memmove(line, line + 7, strlen(line + 7) + 1);
+ /* Selecting namespace to update. */
+ if (head.type == CCS_EXCEPTIONPOLICY || head.type == CCS_PROFILE) {
+ if (*line == '<') {
+ char *cp = strchr(line, ' ');
+ if (cp) {
+ *cp++ = '\0';
+ head.ns = ccs_assign_namespace(line);
+ memmove(line, cp, strlen(cp) + 1);
+ } else
+ head.ns = NULL;
+ /* Don't allow updating if namespace is invalid. */
+ if (!head.ns)
+ return -ENOENT;
+ } else
+ head.ns = &ccs_kernel_namespace;
+ }
+ /* Do the update. */
+ switch (head.type) {
+ case CCS_DOMAINPOLICY:
+ return ccs_write_domain();
+ case CCS_EXCEPTIONPOLICY:
+ return ccs_write_exception();
+ case CCS_STAT:
+ return ccs_write_stat();
+ case CCS_PROFILE:
+ return ccs_write_profile();
+ case CCS_MANAGER:
+ return ccs_write_manager();
+ default:
+ return -ENOSYS;
+ }
+}
+
+/**
+ * ccs_write_control - write() for /proc/ccs/ interface.
+ *
+ * @buffer: Pointer to buffer to read from.
+ * @buffer_len: Size of @buffer.
+ *
+ * Returns @buffer_len on success, negative value otherwise.
+ */
+static void ccs_write_control(char *buffer, const size_t buffer_len)
+{
+ static char *line = NULL;
+ static int line_len = 0;
+ size_t avail_len = buffer_len;
+ while (avail_len > 0) {
+ const char c = *buffer++;
+ avail_len--;
+ line = ccs_realloc(line, line_len + 1);
+ line[line_len++] = c;
+ if (c != '\n')
+ continue;
+ line[line_len - 1] = '\0';
+ line_len = 0;
+ head.data = line;
+ ccs_normalize_line(line);
+ if (!strcmp(line, "reset")) {
+ const u8 type = head.type;
+ memset(&head, 0, sizeof(head));
+ head.reset = true;
+ head.type = type;
+ continue;
+ }
+ /* Don't allow updating policies by non manager programs. */
+ switch (head.type) {
+ case CCS_DOMAINPOLICY:
+ if (ccs_select_domain(line))
+ continue;
+ /* fall through */
+ case CCS_EXCEPTIONPOLICY:
+ if (!strcmp(line, "select transition_only")) {
+ head.print_transition_related_only = true;
+ continue;
+ }
+ }
+ ccs_parse_policy(line);
+ }
+}
+
+/**
+ * ccs_editpolicy_offline_init - Initialize variables for offline daemon.
+ *
+ * Returns nothing.
+ */
+static void ccs_editpolicy_offline_init(coid)
+{
+ static _Bool first = true;
+ int i;
+ if (!first)
+ return;
+ first = false;
+ memset(&head, 0, sizeof(head));
+ memset(&ccs_kernel_domain, 0, sizeof(ccs_kernel_domain));
+ memset(&ccs_kernel_namespace, 0, sizeof(ccs_kernel_namespace));
+ ccs_namespace_enabled = false;
+ ccs_kernel_namespace.name = "<kernel>";
+ for (i = 0; i < CCS_MAX_ACL_GROUPS; i++)
+ INIT_LIST_HEAD(&ccs_kernel_namespace.acl_group[i]);
+ list_add_tail(&ccs_kernel_namespace.namespace_list,
+ &ccs_namespace_list);
+ for (i = 0; i < CCS_MAX_HASH; i++)
+ INIT_LIST_HEAD(&ccs_name_list[i]);
+ INIT_LIST_HEAD(&ccs_kernel_domain.acl_info_list);
+ ccs_kernel_domain.domainname = ccs_savename("<kernel>");
+ list_add_tail(&ccs_kernel_domain.list, &ccs_domain_list);
+ memset(ccs_memory_quota, 0, sizeof(ccs_memory_quota));
+}
+
+/**
+ * ccs_editpolicy_offline_main - Read request and handle policy I/O.
+ *
+ * @fd: Socket file descriptor.
+ *
+ * Returns nothing.
+ */
+static void ccs_editpolicy_offline_main(const int fd)
+{
+ int i;
+ static char buffer[4096];
+ ccs_editpolicy_offline_init();
+ /* Read filename. */
+ for (i = 0; i < sizeof(buffer); i++) {
+ if (read(fd, buffer + i, 1) != 1)
+ return;
+ if (!buffer[i])
+ break;
+ }
+ if (!memchr(buffer, '\0', sizeof(buffer)))
+ return;
+ memset(&head, 0, sizeof(head));
+ head.reset = true;
+ if (!strcmp(buffer, CCS_PROC_POLICY_DOMAIN_POLICY))
+ head.type = CCS_DOMAINPOLICY;
+ else if (!strcmp(buffer, CCS_PROC_POLICY_EXCEPTION_POLICY))
+ head.type = CCS_EXCEPTIONPOLICY;
+ else if (!strcmp(buffer, CCS_PROC_POLICY_PROFILE))
+ head.type = CCS_PROFILE;
+ else if (!strcmp(buffer, CCS_PROC_POLICY_MANAGER))
+ head.type = CCS_MANAGER;
+ else if (!strcmp(buffer, CCS_PROC_POLICY_STAT))
+ head.type = CCS_STAT;
+ else
+ return;
+ /* Return \0 to indicate success. */
+ if (write(fd, "", 1) != 1)
+ return;
+ client_fd = fd;
+ while (1) {
+ struct pollfd pfd = { .fd = fd, .events = POLLIN };
+ int len;
+ int nonzero_len;
+ poll(&pfd, 1, -1);
+ len = recv(fd, buffer, sizeof(buffer), MSG_DONTWAIT);
+ if (len <= 0)
+ break;
+restart:
+ for (nonzero_len = 0 ; nonzero_len < len; nonzero_len++)
+ if (!buffer[nonzero_len])
+ break;
+ if (nonzero_len) {
+ ccs_write_control(buffer, nonzero_len);
+ } else {
+ switch (head.type) {
+ case CCS_DOMAINPOLICY:
+ ccs_read_domain();
+ break;
+ case CCS_EXCEPTIONPOLICY:
+ ccs_read_exception();
+ break;
+ case CCS_STAT:
+ ccs_read_stat();
+ break;
+ case CCS_PROFILE:
+ ccs_read_profile();
+ break;
+ case CCS_MANAGER:
+ ccs_read_manager();
+ break;
+ }
+ /* Flush data. */
+ cprintf("%s", "");
+ /* Return \0 to indicate EOF. */
+ if (write(fd, "", 1) != 1)
+ return;
+ nonzero_len = 1;
}
+ len -= nonzero_len;
+ memmove(buffer, buffer + nonzero_len, len);
+ if (len)
+ goto restart;
+ }
+}
+
+/**
+ * ccs_editpolicy_offline_daemon - Emulate /proc/ccs/ interface.
+ *
+ * @listener: Listener fd. This is a listening PF_INET socket.
+ * @notifier: Notifier fd. This is a pipe's reader side.
+ *
+ * This function does not return.
+ */
+void ccs_editpolicy_offline_daemon(const int listener, const int notifier)
+{
+ while (1) {
+ struct pollfd pfd[2] = {
+ { .fd = listener, .events = POLLIN },
+ { .fd = notifier, .events = POLLIN }
+ };
+ struct sockaddr_in addr;
+ socklen_t size = sizeof(addr);
+ int fd;
+ if (poll(pfd, 2, -1) == EOF ||
+ (pfd[1].revents & (POLLIN | POLLHUP)))
+ _exit(1);
+ fd = accept(listener, (struct sockaddr *) &addr, &size);
+ if (fd == EOF)
+ continue;
+ ccs_editpolicy_offline_main(fd);
+ close(fd);
}
- free(buffer);
- _exit(0);
}
*
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*
- * Version: 1.8.1 2011/04/01
+ * Version: 1.8.2+ 2011/06/26
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License v2 as published by the
(const char *group_name);
/**
- * ccs_find_path_group - Find "path_group" entry.
+ * ccs_find_path_group_ns - Find "path_group" entry.
*
+ * @ns: Pointer to "const struct ccs_path_info".
* @group_name: Name of path group.
*
* Returns pointer to "struct ccs_path_group_entry" if found, NULL otherwise.
*/
-struct ccs_path_group_entry *ccs_find_path_group(const char *group_name)
+struct ccs_path_group_entry *ccs_find_path_group_ns
+(const struct ccs_path_info *ns, const char *group_name)
{
int i;
for (i = 0; i < ccs_path_group_list_len; i++) {
- if (!strcmp(group_name,
+ if (!ccs_pathcmp(ccs_path_group_list[i].ns, ns) &&
+ !strcmp(group_name,
ccs_path_group_list[i].group_name->name))
return &ccs_path_group_list[i];
}
/* Pathname component. */
return ccs_path_matches_pattern(&d, &s);
/* path_group component. */
- group = ccs_find_path_group(s.name + 1);
+ group = ccs_find_path_group_ns(ccs_current_ns, s.name + 1);
if (!group)
return false;
for (i = 0; i < group->member_name_len; i++) {
if (!cp)
return;
if (index == CCS_DIRECTIVE_IPC_SIGNAL && i == 1 &&
- !strncmp(buffer, "<kernel>", 8)) {
- cp = buffer + 8;
+ ccs_domain_def(buffer)) {
+ cp = strchr(buffer, ' ');
+ if (!cp)
+ return;
while (*cp) {
if (*cp++ != ' ' || *cp++ == '/')
continue;
void ccs_editpolicy_optimize(const int current)
{
char *cp;
+ const bool is_exception_list =
+ ccs_current_screen == CCS_SCREEN_EXCEPTION_LIST;
enum ccs_editpolicy_directives s_index;
+ enum ccs_editpolicy_directives s_index2;
int index;
char *s[5];
char *d[5];
s_index = ccs_gacl_list[current].directive;
if (s_index == CCS_DIRECTIVE_NONE)
return;
+ /* Allow acl_group lines to be optimized. */
+ if (is_exception_list &&
+ (s_index < CCS_DIRECTIVE_ACL_GROUP_000 ||
+ s_index > CCS_DIRECTIVE_ACL_GROUP_255))
+ return;
cp = strdup(ccs_gacl_list[current].operand);
if (!cp)
return;
+ s_index2 = s_index;
+ if (is_exception_list)
+ s_index = ccs_find_directive(true, cp);
ccs_tokenize(cp, s, s_index);
ccs_get();
for (index = 0; index < ccs_list_item_count; index++) {
char *line;
- const enum ccs_editpolicy_directives d_index =
+ enum ccs_editpolicy_directives d_index =
ccs_gacl_list[index].directive;
+ enum ccs_editpolicy_directives d_index2;
if (index == current)
/* Skip source. */
continue;
if (ccs_gacl_list[index].selected)
/* Dest already selected. */
continue;
- else if (s_index != d_index)
+ else if (s_index == s_index2 && s_index != d_index)
+ /* Source and dest have different directive. */
+ continue;
+ else if (is_exception_list && s_index2 != d_index)
/* Source and dest have different directive. */
continue;
/* Source and dest have same directive. */
line = ccs_shprintf("%s", ccs_gacl_list[index].operand);
+ d_index2 = d_index;
+ if (is_exception_list)
+ d_index = ccs_find_directive(true, line);
+ if (s_index != d_index || s_index2 != d_index2)
+ /* Source and dest have different directive. */
+ continue;
ccs_tokenize(line, d, d_index);
/* Compare condition part. */
if (strcmp(s[4], d[4]))
if (!ccs_correct_word(group_name))
return -EINVAL;
saved_group_name = ccs_savename(group_name);
- if (!saved_group_name)
- return -ENOMEM;
for (i = 0; i < ccs_address_group_list_len; i++) {
group = &ccs_address_group_list[i];
if (saved_group_name != group->group_name)
if (is_delete)
return -ENOENT;
if (i == ccs_address_group_list_len) {
- void *vp;
- vp = realloc(ccs_address_group_list,
- (ccs_address_group_list_len + 1) *
- sizeof(struct ccs_address_group_entry));
- if (!vp)
- ccs_out_of_memory();
- ccs_address_group_list = vp;
+ ccs_address_group_list =
+ ccs_realloc(ccs_address_group_list,
+ (ccs_address_group_list_len + 1) *
+ sizeof(struct ccs_address_group_entry));
group = &ccs_address_group_list[ccs_address_group_list_len++];
memset(group, 0, sizeof(struct ccs_address_group_entry));
group->group_name = saved_group_name;
}
group->member_name =
- realloc(group->member_name, (group->member_name_len + 1) *
- sizeof(const struct ccs_ip_address_entry));
- if (!group->member_name)
- ccs_out_of_memory();
+ ccs_realloc(group->member_name, (group->member_name_len + 1) *
+ sizeof(const struct ccs_ip_address_entry));
group->member_name[group->member_name_len++] = entry;
return 0;
}
if (!ccs_correct_word(group_name))
return -EINVAL;
saved_group_name = ccs_savename(group_name);
- if (!saved_group_name)
- return -ENOMEM;
for (i = 0; i < ccs_number_group_list_len; i++) {
group = &ccs_number_group_list[i];
if (saved_group_name != group->group_name)
if (is_delete)
return -ENOENT;
if (i == ccs_number_group_list_len) {
- void *vp;
- vp = realloc(ccs_number_group_list,
- (ccs_number_group_list_len + 1) *
- sizeof(struct ccs_number_group_entry));
- if (!vp)
- ccs_out_of_memory();
- ccs_number_group_list = vp;
+ ccs_number_group_list =
+ ccs_realloc(ccs_number_group_list,
+ (ccs_number_group_list_len + 1) *
+ sizeof(struct ccs_number_group_entry));
group = &ccs_number_group_list[ccs_number_group_list_len++];
memset(group, 0, sizeof(struct ccs_number_group_entry));
group->group_name = saved_group_name;
}
- group->member_name = realloc(group->member_name,
- (group->member_name_len + 1) *
- sizeof(const struct ccs_number_entry));
- if (!group->member_name)
- ccs_out_of_memory();
+ group->member_name =
+ ccs_realloc(group->member_name, (group->member_name_len + 1) *
+ sizeof(const struct ccs_number_entry));
group->member_name[group->member_name_len++] = entry;
return 0;
}