Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * NetLabel CIPSO/IPv4 Support
0004  *
0005  * This file defines the CIPSO/IPv4 functions for the NetLabel system.  The
0006  * NetLabel system manages static and dynamic label mappings for network
0007  * protocols such as CIPSO and RIPSO.
0008  *
0009  * Author: Paul Moore <paul@paul-moore.com>
0010  */
0011 
0012 /*
0013  * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
0014  */
0015 
0016 #include <linux/types.h>
0017 #include <linux/socket.h>
0018 #include <linux/string.h>
0019 #include <linux/skbuff.h>
0020 #include <linux/audit.h>
0021 #include <linux/slab.h>
0022 #include <net/sock.h>
0023 #include <net/netlink.h>
0024 #include <net/genetlink.h>
0025 #include <net/netlabel.h>
0026 #include <net/cipso_ipv4.h>
0027 #include <linux/atomic.h>
0028 
0029 #include "netlabel_user.h"
0030 #include "netlabel_cipso_v4.h"
0031 #include "netlabel_mgmt.h"
0032 #include "netlabel_domainhash.h"
0033 
0034 /* Argument struct for cipso_v4_doi_walk() */
0035 struct netlbl_cipsov4_doiwalk_arg {
0036     struct netlink_callback *nl_cb;
0037     struct sk_buff *skb;
0038     u32 seq;
0039 };
0040 
0041 /* Argument struct for netlbl_domhsh_walk() */
0042 struct netlbl_domhsh_walk_arg {
0043     struct netlbl_audit *audit_info;
0044     u32 doi;
0045 };
0046 
0047 /* NetLabel Generic NETLINK CIPSOv4 family */
0048 static struct genl_family netlbl_cipsov4_gnl_family;
0049 /* NetLabel Netlink attribute policy */
0050 static const struct nla_policy netlbl_cipsov4_genl_policy[NLBL_CIPSOV4_A_MAX + 1] = {
0051     [NLBL_CIPSOV4_A_DOI] = { .type = NLA_U32 },
0052     [NLBL_CIPSOV4_A_MTYPE] = { .type = NLA_U32 },
0053     [NLBL_CIPSOV4_A_TAG] = { .type = NLA_U8 },
0054     [NLBL_CIPSOV4_A_TAGLST] = { .type = NLA_NESTED },
0055     [NLBL_CIPSOV4_A_MLSLVLLOC] = { .type = NLA_U32 },
0056     [NLBL_CIPSOV4_A_MLSLVLREM] = { .type = NLA_U32 },
0057     [NLBL_CIPSOV4_A_MLSLVL] = { .type = NLA_NESTED },
0058     [NLBL_CIPSOV4_A_MLSLVLLST] = { .type = NLA_NESTED },
0059     [NLBL_CIPSOV4_A_MLSCATLOC] = { .type = NLA_U32 },
0060     [NLBL_CIPSOV4_A_MLSCATREM] = { .type = NLA_U32 },
0061     [NLBL_CIPSOV4_A_MLSCAT] = { .type = NLA_NESTED },
0062     [NLBL_CIPSOV4_A_MLSCATLST] = { .type = NLA_NESTED },
0063 };
0064 
0065 /*
0066  * Helper Functions
0067  */
0068 
0069 /**
0070  * netlbl_cipsov4_add_common - Parse the common sections of a ADD message
0071  * @info: the Generic NETLINK info block
0072  * @doi_def: the CIPSO V4 DOI definition
0073  *
0074  * Description:
0075  * Parse the common sections of a ADD message and fill in the related values
0076  * in @doi_def.  Returns zero on success, negative values on failure.
0077  *
0078  */
0079 static int netlbl_cipsov4_add_common(struct genl_info *info,
0080                      struct cipso_v4_doi *doi_def)
0081 {
0082     struct nlattr *nla;
0083     int nla_rem;
0084     u32 iter = 0;
0085 
0086     doi_def->doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
0087 
0088     if (nla_validate_nested_deprecated(info->attrs[NLBL_CIPSOV4_A_TAGLST],
0089                        NLBL_CIPSOV4_A_MAX,
0090                        netlbl_cipsov4_genl_policy,
0091                        NULL) != 0)
0092         return -EINVAL;
0093 
0094     nla_for_each_nested(nla, info->attrs[NLBL_CIPSOV4_A_TAGLST], nla_rem)
0095         if (nla_type(nla) == NLBL_CIPSOV4_A_TAG) {
0096             if (iter >= CIPSO_V4_TAG_MAXCNT)
0097                 return -EINVAL;
0098             doi_def->tags[iter++] = nla_get_u8(nla);
0099         }
0100     while (iter < CIPSO_V4_TAG_MAXCNT)
0101         doi_def->tags[iter++] = CIPSO_V4_TAG_INVALID;
0102 
0103     return 0;
0104 }
0105 
0106 /*
0107  * NetLabel Command Handlers
0108  */
0109 
0110 /**
0111  * netlbl_cipsov4_add_std - Adds a CIPSO V4 DOI definition
0112  * @info: the Generic NETLINK info block
0113  * @audit_info: NetLabel audit information
0114  *
0115  * Description:
0116  * Create a new CIPSO_V4_MAP_TRANS DOI definition based on the given ADD
0117  * message and add it to the CIPSO V4 engine.  Return zero on success and
0118  * non-zero on error.
0119  *
0120  */
0121 static int netlbl_cipsov4_add_std(struct genl_info *info,
0122                   struct netlbl_audit *audit_info)
0123 {
0124     int ret_val = -EINVAL;
0125     struct cipso_v4_doi *doi_def = NULL;
0126     struct nlattr *nla_a;
0127     struct nlattr *nla_b;
0128     int nla_a_rem;
0129     int nla_b_rem;
0130     u32 iter;
0131 
0132     if (!info->attrs[NLBL_CIPSOV4_A_TAGLST] ||
0133         !info->attrs[NLBL_CIPSOV4_A_MLSLVLLST])
0134         return -EINVAL;
0135 
0136     if (nla_validate_nested_deprecated(info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
0137                        NLBL_CIPSOV4_A_MAX,
0138                        netlbl_cipsov4_genl_policy,
0139                        NULL) != 0)
0140         return -EINVAL;
0141 
0142     doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
0143     if (doi_def == NULL)
0144         return -ENOMEM;
0145     doi_def->map.std = kzalloc(sizeof(*doi_def->map.std), GFP_KERNEL);
0146     if (doi_def->map.std == NULL) {
0147         kfree(doi_def);
0148         return -ENOMEM;
0149     }
0150     doi_def->type = CIPSO_V4_MAP_TRANS;
0151 
0152     ret_val = netlbl_cipsov4_add_common(info, doi_def);
0153     if (ret_val != 0)
0154         goto add_std_failure;
0155     ret_val = -EINVAL;
0156 
0157     nla_for_each_nested(nla_a,
0158                 info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
0159                 nla_a_rem)
0160         if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSLVL) {
0161             if (nla_validate_nested_deprecated(nla_a,
0162                                NLBL_CIPSOV4_A_MAX,
0163                                netlbl_cipsov4_genl_policy,
0164                                NULL) != 0)
0165                 goto add_std_failure;
0166             nla_for_each_nested(nla_b, nla_a, nla_b_rem)
0167                 switch (nla_type(nla_b)) {
0168                 case NLBL_CIPSOV4_A_MLSLVLLOC:
0169                     if (nla_get_u32(nla_b) >
0170                         CIPSO_V4_MAX_LOC_LVLS)
0171                         goto add_std_failure;
0172                     if (nla_get_u32(nla_b) >=
0173                         doi_def->map.std->lvl.local_size)
0174                          doi_def->map.std->lvl.local_size =
0175                              nla_get_u32(nla_b) + 1;
0176                     break;
0177                 case NLBL_CIPSOV4_A_MLSLVLREM:
0178                     if (nla_get_u32(nla_b) >
0179                         CIPSO_V4_MAX_REM_LVLS)
0180                         goto add_std_failure;
0181                     if (nla_get_u32(nla_b) >=
0182                         doi_def->map.std->lvl.cipso_size)
0183                          doi_def->map.std->lvl.cipso_size =
0184                              nla_get_u32(nla_b) + 1;
0185                     break;
0186                 }
0187         }
0188     doi_def->map.std->lvl.local = kcalloc(doi_def->map.std->lvl.local_size,
0189                           sizeof(u32),
0190                           GFP_KERNEL | __GFP_NOWARN);
0191     if (doi_def->map.std->lvl.local == NULL) {
0192         ret_val = -ENOMEM;
0193         goto add_std_failure;
0194     }
0195     doi_def->map.std->lvl.cipso = kcalloc(doi_def->map.std->lvl.cipso_size,
0196                           sizeof(u32),
0197                           GFP_KERNEL | __GFP_NOWARN);
0198     if (doi_def->map.std->lvl.cipso == NULL) {
0199         ret_val = -ENOMEM;
0200         goto add_std_failure;
0201     }
0202     for (iter = 0; iter < doi_def->map.std->lvl.local_size; iter++)
0203         doi_def->map.std->lvl.local[iter] = CIPSO_V4_INV_LVL;
0204     for (iter = 0; iter < doi_def->map.std->lvl.cipso_size; iter++)
0205         doi_def->map.std->lvl.cipso[iter] = CIPSO_V4_INV_LVL;
0206     nla_for_each_nested(nla_a,
0207                 info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
0208                 nla_a_rem)
0209         if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSLVL) {
0210             struct nlattr *lvl_loc;
0211             struct nlattr *lvl_rem;
0212 
0213             lvl_loc = nla_find_nested(nla_a,
0214                           NLBL_CIPSOV4_A_MLSLVLLOC);
0215             lvl_rem = nla_find_nested(nla_a,
0216                           NLBL_CIPSOV4_A_MLSLVLREM);
0217             if (lvl_loc == NULL || lvl_rem == NULL)
0218                 goto add_std_failure;
0219             doi_def->map.std->lvl.local[nla_get_u32(lvl_loc)] =
0220                 nla_get_u32(lvl_rem);
0221             doi_def->map.std->lvl.cipso[nla_get_u32(lvl_rem)] =
0222                 nla_get_u32(lvl_loc);
0223         }
0224 
0225     if (info->attrs[NLBL_CIPSOV4_A_MLSCATLST]) {
0226         if (nla_validate_nested_deprecated(info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
0227                            NLBL_CIPSOV4_A_MAX,
0228                            netlbl_cipsov4_genl_policy,
0229                            NULL) != 0)
0230             goto add_std_failure;
0231 
0232         nla_for_each_nested(nla_a,
0233                     info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
0234                     nla_a_rem)
0235             if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSCAT) {
0236                 if (nla_validate_nested_deprecated(nla_a,
0237                                    NLBL_CIPSOV4_A_MAX,
0238                                    netlbl_cipsov4_genl_policy,
0239                                    NULL) != 0)
0240                     goto add_std_failure;
0241                 nla_for_each_nested(nla_b, nla_a, nla_b_rem)
0242                     switch (nla_type(nla_b)) {
0243                     case NLBL_CIPSOV4_A_MLSCATLOC:
0244                         if (nla_get_u32(nla_b) >
0245                             CIPSO_V4_MAX_LOC_CATS)
0246                             goto add_std_failure;
0247                         if (nla_get_u32(nla_b) >=
0248                           doi_def->map.std->cat.local_size)
0249                          doi_def->map.std->cat.local_size =
0250                              nla_get_u32(nla_b) + 1;
0251                         break;
0252                     case NLBL_CIPSOV4_A_MLSCATREM:
0253                         if (nla_get_u32(nla_b) >
0254                             CIPSO_V4_MAX_REM_CATS)
0255                             goto add_std_failure;
0256                         if (nla_get_u32(nla_b) >=
0257                           doi_def->map.std->cat.cipso_size)
0258                          doi_def->map.std->cat.cipso_size =
0259                              nla_get_u32(nla_b) + 1;
0260                         break;
0261                     }
0262             }
0263         doi_def->map.std->cat.local = kcalloc(
0264                           doi_def->map.std->cat.local_size,
0265                           sizeof(u32),
0266                           GFP_KERNEL | __GFP_NOWARN);
0267         if (doi_def->map.std->cat.local == NULL) {
0268             ret_val = -ENOMEM;
0269             goto add_std_failure;
0270         }
0271         doi_def->map.std->cat.cipso = kcalloc(
0272                           doi_def->map.std->cat.cipso_size,
0273                           sizeof(u32),
0274                           GFP_KERNEL | __GFP_NOWARN);
0275         if (doi_def->map.std->cat.cipso == NULL) {
0276             ret_val = -ENOMEM;
0277             goto add_std_failure;
0278         }
0279         for (iter = 0; iter < doi_def->map.std->cat.local_size; iter++)
0280             doi_def->map.std->cat.local[iter] = CIPSO_V4_INV_CAT;
0281         for (iter = 0; iter < doi_def->map.std->cat.cipso_size; iter++)
0282             doi_def->map.std->cat.cipso[iter] = CIPSO_V4_INV_CAT;
0283         nla_for_each_nested(nla_a,
0284                     info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
0285                     nla_a_rem)
0286             if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSCAT) {
0287                 struct nlattr *cat_loc;
0288                 struct nlattr *cat_rem;
0289 
0290                 cat_loc = nla_find_nested(nla_a,
0291                              NLBL_CIPSOV4_A_MLSCATLOC);
0292                 cat_rem = nla_find_nested(nla_a,
0293                              NLBL_CIPSOV4_A_MLSCATREM);
0294                 if (cat_loc == NULL || cat_rem == NULL)
0295                     goto add_std_failure;
0296                 doi_def->map.std->cat.local[
0297                             nla_get_u32(cat_loc)] =
0298                     nla_get_u32(cat_rem);
0299                 doi_def->map.std->cat.cipso[
0300                             nla_get_u32(cat_rem)] =
0301                     nla_get_u32(cat_loc);
0302             }
0303     }
0304 
0305     ret_val = cipso_v4_doi_add(doi_def, audit_info);
0306     if (ret_val != 0)
0307         goto add_std_failure;
0308     return 0;
0309 
0310 add_std_failure:
0311     cipso_v4_doi_free(doi_def);
0312     return ret_val;
0313 }
0314 
0315 /**
0316  * netlbl_cipsov4_add_pass - Adds a CIPSO V4 DOI definition
0317  * @info: the Generic NETLINK info block
0318  * @audit_info: NetLabel audit information
0319  *
0320  * Description:
0321  * Create a new CIPSO_V4_MAP_PASS DOI definition based on the given ADD message
0322  * and add it to the CIPSO V4 engine.  Return zero on success and non-zero on
0323  * error.
0324  *
0325  */
0326 static int netlbl_cipsov4_add_pass(struct genl_info *info,
0327                    struct netlbl_audit *audit_info)
0328 {
0329     int ret_val;
0330     struct cipso_v4_doi *doi_def = NULL;
0331 
0332     if (!info->attrs[NLBL_CIPSOV4_A_TAGLST])
0333         return -EINVAL;
0334 
0335     doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
0336     if (doi_def == NULL)
0337         return -ENOMEM;
0338     doi_def->type = CIPSO_V4_MAP_PASS;
0339 
0340     ret_val = netlbl_cipsov4_add_common(info, doi_def);
0341     if (ret_val != 0)
0342         goto add_pass_failure;
0343 
0344     ret_val = cipso_v4_doi_add(doi_def, audit_info);
0345     if (ret_val != 0)
0346         goto add_pass_failure;
0347     return 0;
0348 
0349 add_pass_failure:
0350     cipso_v4_doi_free(doi_def);
0351     return ret_val;
0352 }
0353 
0354 /**
0355  * netlbl_cipsov4_add_local - Adds a CIPSO V4 DOI definition
0356  * @info: the Generic NETLINK info block
0357  * @audit_info: NetLabel audit information
0358  *
0359  * Description:
0360  * Create a new CIPSO_V4_MAP_LOCAL DOI definition based on the given ADD
0361  * message and add it to the CIPSO V4 engine.  Return zero on success and
0362  * non-zero on error.
0363  *
0364  */
0365 static int netlbl_cipsov4_add_local(struct genl_info *info,
0366                     struct netlbl_audit *audit_info)
0367 {
0368     int ret_val;
0369     struct cipso_v4_doi *doi_def = NULL;
0370 
0371     if (!info->attrs[NLBL_CIPSOV4_A_TAGLST])
0372         return -EINVAL;
0373 
0374     doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
0375     if (doi_def == NULL)
0376         return -ENOMEM;
0377     doi_def->type = CIPSO_V4_MAP_LOCAL;
0378 
0379     ret_val = netlbl_cipsov4_add_common(info, doi_def);
0380     if (ret_val != 0)
0381         goto add_local_failure;
0382 
0383     ret_val = cipso_v4_doi_add(doi_def, audit_info);
0384     if (ret_val != 0)
0385         goto add_local_failure;
0386     return 0;
0387 
0388 add_local_failure:
0389     cipso_v4_doi_free(doi_def);
0390     return ret_val;
0391 }
0392 
0393 /**
0394  * netlbl_cipsov4_add - Handle an ADD message
0395  * @skb: the NETLINK buffer
0396  * @info: the Generic NETLINK info block
0397  *
0398  * Description:
0399  * Create a new DOI definition based on the given ADD message and add it to the
0400  * CIPSO V4 engine.  Returns zero on success, negative values on failure.
0401  *
0402  */
0403 static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info)
0404 
0405 {
0406     int ret_val = -EINVAL;
0407     struct netlbl_audit audit_info;
0408 
0409     if (!info->attrs[NLBL_CIPSOV4_A_DOI] ||
0410         !info->attrs[NLBL_CIPSOV4_A_MTYPE])
0411         return -EINVAL;
0412 
0413     netlbl_netlink_auditinfo(&audit_info);
0414     switch (nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE])) {
0415     case CIPSO_V4_MAP_TRANS:
0416         ret_val = netlbl_cipsov4_add_std(info, &audit_info);
0417         break;
0418     case CIPSO_V4_MAP_PASS:
0419         ret_val = netlbl_cipsov4_add_pass(info, &audit_info);
0420         break;
0421     case CIPSO_V4_MAP_LOCAL:
0422         ret_val = netlbl_cipsov4_add_local(info, &audit_info);
0423         break;
0424     }
0425     if (ret_val == 0)
0426         atomic_inc(&netlabel_mgmt_protocount);
0427 
0428     return ret_val;
0429 }
0430 
0431 /**
0432  * netlbl_cipsov4_list - Handle a LIST message
0433  * @skb: the NETLINK buffer
0434  * @info: the Generic NETLINK info block
0435  *
0436  * Description:
0437  * Process a user generated LIST message and respond accordingly.  While the
0438  * response message generated by the kernel is straightforward, determining
0439  * before hand the size of the buffer to allocate is not (we have to generate
0440  * the message to know the size).  In order to keep this function sane what we
0441  * do is allocate a buffer of NLMSG_GOODSIZE and try to fit the response in
0442  * that size, if we fail then we restart with a larger buffer and try again.
0443  * We continue in this manner until we hit a limit of failed attempts then we
0444  * give up and just send an error message.  Returns zero on success and
0445  * negative values on error.
0446  *
0447  */
0448 static int netlbl_cipsov4_list(struct sk_buff *skb, struct genl_info *info)
0449 {
0450     int ret_val;
0451     struct sk_buff *ans_skb = NULL;
0452     u32 nlsze_mult = 1;
0453     void *data;
0454     u32 doi;
0455     struct nlattr *nla_a;
0456     struct nlattr *nla_b;
0457     struct cipso_v4_doi *doi_def;
0458     u32 iter;
0459 
0460     if (!info->attrs[NLBL_CIPSOV4_A_DOI]) {
0461         ret_val = -EINVAL;
0462         goto list_failure;
0463     }
0464 
0465 list_start:
0466     ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE * nlsze_mult, GFP_KERNEL);
0467     if (ans_skb == NULL) {
0468         ret_val = -ENOMEM;
0469         goto list_failure;
0470     }
0471     data = genlmsg_put_reply(ans_skb, info, &netlbl_cipsov4_gnl_family,
0472                  0, NLBL_CIPSOV4_C_LIST);
0473     if (data == NULL) {
0474         ret_val = -ENOMEM;
0475         goto list_failure;
0476     }
0477 
0478     doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
0479 
0480     rcu_read_lock();
0481     doi_def = cipso_v4_doi_getdef(doi);
0482     if (doi_def == NULL) {
0483         ret_val = -EINVAL;
0484         goto list_failure_lock;
0485     }
0486 
0487     ret_val = nla_put_u32(ans_skb, NLBL_CIPSOV4_A_MTYPE, doi_def->type);
0488     if (ret_val != 0)
0489         goto list_failure_lock;
0490 
0491     nla_a = nla_nest_start_noflag(ans_skb, NLBL_CIPSOV4_A_TAGLST);
0492     if (nla_a == NULL) {
0493         ret_val = -ENOMEM;
0494         goto list_failure_lock;
0495     }
0496     for (iter = 0;
0497          iter < CIPSO_V4_TAG_MAXCNT &&
0498            doi_def->tags[iter] != CIPSO_V4_TAG_INVALID;
0499          iter++) {
0500         ret_val = nla_put_u8(ans_skb,
0501                      NLBL_CIPSOV4_A_TAG,
0502                      doi_def->tags[iter]);
0503         if (ret_val != 0)
0504             goto list_failure_lock;
0505     }
0506     nla_nest_end(ans_skb, nla_a);
0507 
0508     switch (doi_def->type) {
0509     case CIPSO_V4_MAP_TRANS:
0510         nla_a = nla_nest_start_noflag(ans_skb,
0511                           NLBL_CIPSOV4_A_MLSLVLLST);
0512         if (nla_a == NULL) {
0513             ret_val = -ENOMEM;
0514             goto list_failure_lock;
0515         }
0516         for (iter = 0;
0517              iter < doi_def->map.std->lvl.local_size;
0518              iter++) {
0519             if (doi_def->map.std->lvl.local[iter] ==
0520                 CIPSO_V4_INV_LVL)
0521                 continue;
0522 
0523             nla_b = nla_nest_start_noflag(ans_skb,
0524                               NLBL_CIPSOV4_A_MLSLVL);
0525             if (nla_b == NULL) {
0526                 ret_val = -ENOMEM;
0527                 goto list_retry;
0528             }
0529             ret_val = nla_put_u32(ans_skb,
0530                           NLBL_CIPSOV4_A_MLSLVLLOC,
0531                           iter);
0532             if (ret_val != 0)
0533                 goto list_retry;
0534             ret_val = nla_put_u32(ans_skb,
0535                         NLBL_CIPSOV4_A_MLSLVLREM,
0536                         doi_def->map.std->lvl.local[iter]);
0537             if (ret_val != 0)
0538                 goto list_retry;
0539             nla_nest_end(ans_skb, nla_b);
0540         }
0541         nla_nest_end(ans_skb, nla_a);
0542 
0543         nla_a = nla_nest_start_noflag(ans_skb,
0544                           NLBL_CIPSOV4_A_MLSCATLST);
0545         if (nla_a == NULL) {
0546             ret_val = -ENOMEM;
0547             goto list_retry;
0548         }
0549         for (iter = 0;
0550              iter < doi_def->map.std->cat.local_size;
0551              iter++) {
0552             if (doi_def->map.std->cat.local[iter] ==
0553                 CIPSO_V4_INV_CAT)
0554                 continue;
0555 
0556             nla_b = nla_nest_start_noflag(ans_skb,
0557                               NLBL_CIPSOV4_A_MLSCAT);
0558             if (nla_b == NULL) {
0559                 ret_val = -ENOMEM;
0560                 goto list_retry;
0561             }
0562             ret_val = nla_put_u32(ans_skb,
0563                           NLBL_CIPSOV4_A_MLSCATLOC,
0564                           iter);
0565             if (ret_val != 0)
0566                 goto list_retry;
0567             ret_val = nla_put_u32(ans_skb,
0568                         NLBL_CIPSOV4_A_MLSCATREM,
0569                         doi_def->map.std->cat.local[iter]);
0570             if (ret_val != 0)
0571                 goto list_retry;
0572             nla_nest_end(ans_skb, nla_b);
0573         }
0574         nla_nest_end(ans_skb, nla_a);
0575 
0576         break;
0577     }
0578     cipso_v4_doi_putdef(doi_def);
0579     rcu_read_unlock();
0580 
0581     genlmsg_end(ans_skb, data);
0582     return genlmsg_reply(ans_skb, info);
0583 
0584 list_retry:
0585     /* XXX - this limit is a guesstimate */
0586     if (nlsze_mult < 4) {
0587         cipso_v4_doi_putdef(doi_def);
0588         rcu_read_unlock();
0589         kfree_skb(ans_skb);
0590         nlsze_mult *= 2;
0591         goto list_start;
0592     }
0593 list_failure_lock:
0594     cipso_v4_doi_putdef(doi_def);
0595     rcu_read_unlock();
0596 list_failure:
0597     kfree_skb(ans_skb);
0598     return ret_val;
0599 }
0600 
0601 /**
0602  * netlbl_cipsov4_listall_cb - cipso_v4_doi_walk() callback for LISTALL
0603  * @doi_def: the CIPSOv4 DOI definition
0604  * @arg: the netlbl_cipsov4_doiwalk_arg structure
0605  *
0606  * Description:
0607  * This function is designed to be used as a callback to the
0608  * cipso_v4_doi_walk() function for use in generating a response for a LISTALL
0609  * message.  Returns the size of the message on success, negative values on
0610  * failure.
0611  *
0612  */
0613 static int netlbl_cipsov4_listall_cb(struct cipso_v4_doi *doi_def, void *arg)
0614 {
0615     int ret_val = -ENOMEM;
0616     struct netlbl_cipsov4_doiwalk_arg *cb_arg = arg;
0617     void *data;
0618 
0619     data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid,
0620                cb_arg->seq, &netlbl_cipsov4_gnl_family,
0621                NLM_F_MULTI, NLBL_CIPSOV4_C_LISTALL);
0622     if (data == NULL)
0623         goto listall_cb_failure;
0624 
0625     ret_val = nla_put_u32(cb_arg->skb, NLBL_CIPSOV4_A_DOI, doi_def->doi);
0626     if (ret_val != 0)
0627         goto listall_cb_failure;
0628     ret_val = nla_put_u32(cb_arg->skb,
0629                   NLBL_CIPSOV4_A_MTYPE,
0630                   doi_def->type);
0631     if (ret_val != 0)
0632         goto listall_cb_failure;
0633 
0634     genlmsg_end(cb_arg->skb, data);
0635     return 0;
0636 
0637 listall_cb_failure:
0638     genlmsg_cancel(cb_arg->skb, data);
0639     return ret_val;
0640 }
0641 
0642 /**
0643  * netlbl_cipsov4_listall - Handle a LISTALL message
0644  * @skb: the NETLINK buffer
0645  * @cb: the NETLINK callback
0646  *
0647  * Description:
0648  * Process a user generated LISTALL message and respond accordingly.  Returns
0649  * zero on success and negative values on error.
0650  *
0651  */
0652 static int netlbl_cipsov4_listall(struct sk_buff *skb,
0653                   struct netlink_callback *cb)
0654 {
0655     struct netlbl_cipsov4_doiwalk_arg cb_arg;
0656     u32 doi_skip = cb->args[0];
0657 
0658     cb_arg.nl_cb = cb;
0659     cb_arg.skb = skb;
0660     cb_arg.seq = cb->nlh->nlmsg_seq;
0661 
0662     cipso_v4_doi_walk(&doi_skip, netlbl_cipsov4_listall_cb, &cb_arg);
0663 
0664     cb->args[0] = doi_skip;
0665     return skb->len;
0666 }
0667 
0668 /**
0669  * netlbl_cipsov4_remove_cb - netlbl_cipsov4_remove() callback for REMOVE
0670  * @entry: LSM domain mapping entry
0671  * @arg: the netlbl_domhsh_walk_arg structure
0672  *
0673  * Description:
0674  * This function is intended for use by netlbl_cipsov4_remove() as the callback
0675  * for the netlbl_domhsh_walk() function; it removes LSM domain map entries
0676  * which are associated with the CIPSO DOI specified in @arg.  Returns zero on
0677  * success, negative values on failure.
0678  *
0679  */
0680 static int netlbl_cipsov4_remove_cb(struct netlbl_dom_map *entry, void *arg)
0681 {
0682     struct netlbl_domhsh_walk_arg *cb_arg = arg;
0683 
0684     if (entry->def.type == NETLBL_NLTYPE_CIPSOV4 &&
0685         entry->def.cipso->doi == cb_arg->doi)
0686         return netlbl_domhsh_remove_entry(entry, cb_arg->audit_info);
0687 
0688     return 0;
0689 }
0690 
0691 /**
0692  * netlbl_cipsov4_remove - Handle a REMOVE message
0693  * @skb: the NETLINK buffer
0694  * @info: the Generic NETLINK info block
0695  *
0696  * Description:
0697  * Process a user generated REMOVE message and respond accordingly.  Returns
0698  * zero on success, negative values on failure.
0699  *
0700  */
0701 static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
0702 {
0703     int ret_val = -EINVAL;
0704     struct netlbl_domhsh_walk_arg cb_arg;
0705     struct netlbl_audit audit_info;
0706     u32 skip_bkt = 0;
0707     u32 skip_chain = 0;
0708 
0709     if (!info->attrs[NLBL_CIPSOV4_A_DOI])
0710         return -EINVAL;
0711 
0712     netlbl_netlink_auditinfo(&audit_info);
0713     cb_arg.doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
0714     cb_arg.audit_info = &audit_info;
0715     ret_val = netlbl_domhsh_walk(&skip_bkt, &skip_chain,
0716                      netlbl_cipsov4_remove_cb, &cb_arg);
0717     if (ret_val == 0 || ret_val == -ENOENT) {
0718         ret_val = cipso_v4_doi_remove(cb_arg.doi, &audit_info);
0719         if (ret_val == 0)
0720             atomic_dec(&netlabel_mgmt_protocount);
0721     }
0722 
0723     return ret_val;
0724 }
0725 
0726 /*
0727  * NetLabel Generic NETLINK Command Definitions
0728  */
0729 
0730 static const struct genl_small_ops netlbl_cipsov4_ops[] = {
0731     {
0732     .cmd = NLBL_CIPSOV4_C_ADD,
0733     .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0734     .flags = GENL_ADMIN_PERM,
0735     .doit = netlbl_cipsov4_add,
0736     .dumpit = NULL,
0737     },
0738     {
0739     .cmd = NLBL_CIPSOV4_C_REMOVE,
0740     .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0741     .flags = GENL_ADMIN_PERM,
0742     .doit = netlbl_cipsov4_remove,
0743     .dumpit = NULL,
0744     },
0745     {
0746     .cmd = NLBL_CIPSOV4_C_LIST,
0747     .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0748     .flags = 0,
0749     .doit = netlbl_cipsov4_list,
0750     .dumpit = NULL,
0751     },
0752     {
0753     .cmd = NLBL_CIPSOV4_C_LISTALL,
0754     .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0755     .flags = 0,
0756     .doit = NULL,
0757     .dumpit = netlbl_cipsov4_listall,
0758     },
0759 };
0760 
0761 static struct genl_family netlbl_cipsov4_gnl_family __ro_after_init = {
0762     .hdrsize = 0,
0763     .name = NETLBL_NLTYPE_CIPSOV4_NAME,
0764     .version = NETLBL_PROTO_VERSION,
0765     .maxattr = NLBL_CIPSOV4_A_MAX,
0766     .policy = netlbl_cipsov4_genl_policy,
0767     .module = THIS_MODULE,
0768     .small_ops = netlbl_cipsov4_ops,
0769     .n_small_ops = ARRAY_SIZE(netlbl_cipsov4_ops),
0770 };
0771 
0772 /*
0773  * NetLabel Generic NETLINK Protocol Functions
0774  */
0775 
0776 /**
0777  * netlbl_cipsov4_genl_init - Register the CIPSOv4 NetLabel component
0778  *
0779  * Description:
0780  * Register the CIPSOv4 packet NetLabel component with the Generic NETLINK
0781  * mechanism.  Returns zero on success, negative values on failure.
0782  *
0783  */
0784 int __init netlbl_cipsov4_genl_init(void)
0785 {
0786     return genl_register_family(&netlbl_cipsov4_gnl_family);
0787 }