1cb72d382SHuw Davies /* 2cb72d382SHuw Davies * CALIPSO - Common Architecture Label IPv6 Security Option 3cb72d382SHuw Davies * 4cb72d382SHuw Davies * This is an implementation of the CALIPSO protocol as specified in 5cb72d382SHuw Davies * RFC 5570. 6cb72d382SHuw Davies * 7cb72d382SHuw Davies * Authors: Paul Moore <paul.moore@hp.com> 8cb72d382SHuw Davies * Huw Davies <huw@codeweavers.com> 9cb72d382SHuw Davies * 10cb72d382SHuw Davies */ 11cb72d382SHuw Davies 12cb72d382SHuw Davies /* (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008 13cb72d382SHuw Davies * (c) Copyright Huw Davies <huw@codeweavers.com>, 2015 14cb72d382SHuw Davies * 15cb72d382SHuw Davies * This program is free software; you can redistribute it and/or modify 16cb72d382SHuw Davies * it under the terms of the GNU General Public License as published by 17cb72d382SHuw Davies * the Free Software Foundation; either version 2 of the License, or 18cb72d382SHuw Davies * (at your option) any later version. 19cb72d382SHuw Davies * 20cb72d382SHuw Davies * This program is distributed in the hope that it will be useful, 21cb72d382SHuw Davies * but WITHOUT ANY WARRANTY; without even the implied warranty of 22cb72d382SHuw Davies * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 23cb72d382SHuw Davies * the GNU General Public License for more details. 24cb72d382SHuw Davies * 25cb72d382SHuw Davies * You should have received a copy of the GNU General Public License 26cb72d382SHuw Davies * along with this program; if not, see <http://www.gnu.org/licenses/>. 27cb72d382SHuw Davies * 28cb72d382SHuw Davies */ 29cb72d382SHuw Davies 30cb72d382SHuw Davies #include <linux/init.h> 31cb72d382SHuw Davies #include <linux/types.h> 32cb72d382SHuw Davies #include <linux/rcupdate.h> 33cb72d382SHuw Davies #include <linux/list.h> 34cb72d382SHuw Davies #include <linux/spinlock.h> 35cb72d382SHuw Davies #include <linux/string.h> 36cb72d382SHuw Davies #include <linux/jhash.h> 37cb72d382SHuw Davies #include <linux/audit.h> 38cb72d382SHuw Davies #include <linux/slab.h> 39cb72d382SHuw Davies #include <net/ip.h> 40cb72d382SHuw Davies #include <net/icmp.h> 41cb72d382SHuw Davies #include <net/tcp.h> 42cb72d382SHuw Davies #include <net/netlabel.h> 43cb72d382SHuw Davies #include <net/calipso.h> 44cb72d382SHuw Davies #include <linux/atomic.h> 45cb72d382SHuw Davies #include <linux/bug.h> 46cb72d382SHuw Davies #include <asm/unaligned.h> 47*ceba1832SHuw Davies #include <linux/crc-ccitt.h> 48*ceba1832SHuw Davies 49*ceba1832SHuw Davies /* Maximium size of the calipso option including 50*ceba1832SHuw Davies * the two-byte TLV header. 51*ceba1832SHuw Davies */ 52*ceba1832SHuw Davies #define CALIPSO_OPT_LEN_MAX (2 + 252) 53*ceba1832SHuw Davies 54*ceba1832SHuw Davies /* Size of the minimum calipso option including 55*ceba1832SHuw Davies * the two-byte TLV header. 56*ceba1832SHuw Davies */ 57*ceba1832SHuw Davies #define CALIPSO_HDR_LEN (2 + 8) 58*ceba1832SHuw Davies 59*ceba1832SHuw Davies /* Maximium size of the calipso option including 60*ceba1832SHuw Davies * the two-byte TLV header and upto 3 bytes of 61*ceba1832SHuw Davies * leading pad and 7 bytes of trailing pad. 62*ceba1832SHuw Davies */ 63*ceba1832SHuw Davies #define CALIPSO_OPT_LEN_MAX_WITH_PAD (3 + CALIPSO_OPT_LEN_MAX + 7) 64*ceba1832SHuw Davies 65cb72d382SHuw Davies 66cb72d382SHuw Davies /* List of available DOI definitions */ 67cb72d382SHuw Davies static DEFINE_SPINLOCK(calipso_doi_list_lock); 68cb72d382SHuw Davies static LIST_HEAD(calipso_doi_list); 69cb72d382SHuw Davies 70cb72d382SHuw Davies /* DOI List Functions 71cb72d382SHuw Davies */ 72cb72d382SHuw Davies 73cb72d382SHuw Davies /** 74cb72d382SHuw Davies * calipso_doi_search - Searches for a DOI definition 75cb72d382SHuw Davies * @doi: the DOI to search for 76cb72d382SHuw Davies * 77cb72d382SHuw Davies * Description: 78cb72d382SHuw Davies * Search the DOI definition list for a DOI definition with a DOI value that 79cb72d382SHuw Davies * matches @doi. The caller is responsible for calling rcu_read_[un]lock(). 80cb72d382SHuw Davies * Returns a pointer to the DOI definition on success and NULL on failure. 81cb72d382SHuw Davies */ 82cb72d382SHuw Davies static struct calipso_doi *calipso_doi_search(u32 doi) 83cb72d382SHuw Davies { 84cb72d382SHuw Davies struct calipso_doi *iter; 85cb72d382SHuw Davies 86cb72d382SHuw Davies list_for_each_entry_rcu(iter, &calipso_doi_list, list) 87cb72d382SHuw Davies if (iter->doi == doi && atomic_read(&iter->refcount)) 88cb72d382SHuw Davies return iter; 89cb72d382SHuw Davies return NULL; 90cb72d382SHuw Davies } 91cb72d382SHuw Davies 92cb72d382SHuw Davies /** 93cb72d382SHuw Davies * calipso_doi_add - Add a new DOI to the CALIPSO protocol engine 94cb72d382SHuw Davies * @doi_def: the DOI structure 95cb72d382SHuw Davies * @audit_info: NetLabel audit information 96cb72d382SHuw Davies * 97cb72d382SHuw Davies * Description: 98cb72d382SHuw Davies * The caller defines a new DOI for use by the CALIPSO engine and calls this 99cb72d382SHuw Davies * function to add it to the list of acceptable domains. The caller must 100cb72d382SHuw Davies * ensure that the mapping table specified in @doi_def->map meets all of the 101cb72d382SHuw Davies * requirements of the mapping type (see calipso.h for details). Returns 102cb72d382SHuw Davies * zero on success and non-zero on failure. 103cb72d382SHuw Davies * 104cb72d382SHuw Davies */ 105cb72d382SHuw Davies static int calipso_doi_add(struct calipso_doi *doi_def, 106cb72d382SHuw Davies struct netlbl_audit *audit_info) 107cb72d382SHuw Davies { 108cb72d382SHuw Davies int ret_val = -EINVAL; 109cb72d382SHuw Davies u32 doi; 110cb72d382SHuw Davies u32 doi_type; 111cb72d382SHuw Davies struct audit_buffer *audit_buf; 112cb72d382SHuw Davies 113cb72d382SHuw Davies doi = doi_def->doi; 114cb72d382SHuw Davies doi_type = doi_def->type; 115cb72d382SHuw Davies 116cb72d382SHuw Davies if (doi_def->doi == CALIPSO_DOI_UNKNOWN) 117cb72d382SHuw Davies goto doi_add_return; 118cb72d382SHuw Davies 119cb72d382SHuw Davies atomic_set(&doi_def->refcount, 1); 120cb72d382SHuw Davies 121cb72d382SHuw Davies spin_lock(&calipso_doi_list_lock); 122cb72d382SHuw Davies if (calipso_doi_search(doi_def->doi)) { 123cb72d382SHuw Davies spin_unlock(&calipso_doi_list_lock); 124cb72d382SHuw Davies ret_val = -EEXIST; 125cb72d382SHuw Davies goto doi_add_return; 126cb72d382SHuw Davies } 127cb72d382SHuw Davies list_add_tail_rcu(&doi_def->list, &calipso_doi_list); 128cb72d382SHuw Davies spin_unlock(&calipso_doi_list_lock); 129cb72d382SHuw Davies ret_val = 0; 130cb72d382SHuw Davies 131cb72d382SHuw Davies doi_add_return: 132cb72d382SHuw Davies audit_buf = netlbl_audit_start(AUDIT_MAC_CALIPSO_ADD, audit_info); 133cb72d382SHuw Davies if (audit_buf) { 134cb72d382SHuw Davies const char *type_str; 135cb72d382SHuw Davies 136cb72d382SHuw Davies switch (doi_type) { 137cb72d382SHuw Davies case CALIPSO_MAP_PASS: 138cb72d382SHuw Davies type_str = "pass"; 139cb72d382SHuw Davies break; 140cb72d382SHuw Davies default: 141cb72d382SHuw Davies type_str = "(unknown)"; 142cb72d382SHuw Davies } 143cb72d382SHuw Davies audit_log_format(audit_buf, 144cb72d382SHuw Davies " calipso_doi=%u calipso_type=%s res=%u", 145cb72d382SHuw Davies doi, type_str, ret_val == 0 ? 1 : 0); 146cb72d382SHuw Davies audit_log_end(audit_buf); 147cb72d382SHuw Davies } 148cb72d382SHuw Davies 149cb72d382SHuw Davies return ret_val; 150cb72d382SHuw Davies } 151cb72d382SHuw Davies 152cb72d382SHuw Davies /** 153cb72d382SHuw Davies * calipso_doi_free - Frees a DOI definition 154cb72d382SHuw Davies * @doi_def: the DOI definition 155cb72d382SHuw Davies * 156cb72d382SHuw Davies * Description: 157cb72d382SHuw Davies * This function frees all of the memory associated with a DOI definition. 158cb72d382SHuw Davies * 159cb72d382SHuw Davies */ 160cb72d382SHuw Davies static void calipso_doi_free(struct calipso_doi *doi_def) 161cb72d382SHuw Davies { 162cb72d382SHuw Davies kfree(doi_def); 163cb72d382SHuw Davies } 164cb72d382SHuw Davies 165a5e34490SHuw Davies /** 166a5e34490SHuw Davies * calipso_doi_free_rcu - Frees a DOI definition via the RCU pointer 167a5e34490SHuw Davies * @entry: the entry's RCU field 168a5e34490SHuw Davies * 169a5e34490SHuw Davies * Description: 170a5e34490SHuw Davies * This function is designed to be used as a callback to the call_rcu() 171a5e34490SHuw Davies * function so that the memory allocated to the DOI definition can be released 172a5e34490SHuw Davies * safely. 173a5e34490SHuw Davies * 174a5e34490SHuw Davies */ 175a5e34490SHuw Davies static void calipso_doi_free_rcu(struct rcu_head *entry) 176a5e34490SHuw Davies { 177a5e34490SHuw Davies struct calipso_doi *doi_def; 178a5e34490SHuw Davies 179a5e34490SHuw Davies doi_def = container_of(entry, struct calipso_doi, rcu); 180a5e34490SHuw Davies calipso_doi_free(doi_def); 181a5e34490SHuw Davies } 182a5e34490SHuw Davies 183a5e34490SHuw Davies /** 184d7cce015SHuw Davies * calipso_doi_remove - Remove an existing DOI from the CALIPSO protocol engine 185d7cce015SHuw Davies * @doi: the DOI value 186d7cce015SHuw Davies * @audit_secid: the LSM secid to use in the audit message 187d7cce015SHuw Davies * 188d7cce015SHuw Davies * Description: 189d7cce015SHuw Davies * Removes a DOI definition from the CALIPSO engine. The NetLabel routines will 190d7cce015SHuw Davies * be called to release their own LSM domain mappings as well as our own 191d7cce015SHuw Davies * domain list. Returns zero on success and negative values on failure. 192d7cce015SHuw Davies * 193d7cce015SHuw Davies */ 194d7cce015SHuw Davies static int calipso_doi_remove(u32 doi, struct netlbl_audit *audit_info) 195d7cce015SHuw Davies { 196d7cce015SHuw Davies int ret_val; 197d7cce015SHuw Davies struct calipso_doi *doi_def; 198d7cce015SHuw Davies struct audit_buffer *audit_buf; 199d7cce015SHuw Davies 200d7cce015SHuw Davies spin_lock(&calipso_doi_list_lock); 201d7cce015SHuw Davies doi_def = calipso_doi_search(doi); 202d7cce015SHuw Davies if (!doi_def) { 203d7cce015SHuw Davies spin_unlock(&calipso_doi_list_lock); 204d7cce015SHuw Davies ret_val = -ENOENT; 205d7cce015SHuw Davies goto doi_remove_return; 206d7cce015SHuw Davies } 207d7cce015SHuw Davies if (!atomic_dec_and_test(&doi_def->refcount)) { 208d7cce015SHuw Davies spin_unlock(&calipso_doi_list_lock); 209d7cce015SHuw Davies ret_val = -EBUSY; 210d7cce015SHuw Davies goto doi_remove_return; 211d7cce015SHuw Davies } 212d7cce015SHuw Davies list_del_rcu(&doi_def->list); 213d7cce015SHuw Davies spin_unlock(&calipso_doi_list_lock); 214d7cce015SHuw Davies 215d7cce015SHuw Davies call_rcu(&doi_def->rcu, calipso_doi_free_rcu); 216d7cce015SHuw Davies ret_val = 0; 217d7cce015SHuw Davies 218d7cce015SHuw Davies doi_remove_return: 219d7cce015SHuw Davies audit_buf = netlbl_audit_start(AUDIT_MAC_CALIPSO_DEL, audit_info); 220d7cce015SHuw Davies if (audit_buf) { 221d7cce015SHuw Davies audit_log_format(audit_buf, 222d7cce015SHuw Davies " calipso_doi=%u res=%u", 223d7cce015SHuw Davies doi, ret_val == 0 ? 1 : 0); 224d7cce015SHuw Davies audit_log_end(audit_buf); 225d7cce015SHuw Davies } 226d7cce015SHuw Davies 227d7cce015SHuw Davies return ret_val; 228d7cce015SHuw Davies } 229d7cce015SHuw Davies 230d7cce015SHuw Davies /** 231a5e34490SHuw Davies * calipso_doi_getdef - Returns a reference to a valid DOI definition 232a5e34490SHuw Davies * @doi: the DOI value 233a5e34490SHuw Davies * 234a5e34490SHuw Davies * Description: 235a5e34490SHuw Davies * Searches for a valid DOI definition and if one is found it is returned to 236a5e34490SHuw Davies * the caller. Otherwise NULL is returned. The caller must ensure that 237a5e34490SHuw Davies * calipso_doi_putdef() is called when the caller is done. 238a5e34490SHuw Davies * 239a5e34490SHuw Davies */ 240a5e34490SHuw Davies static struct calipso_doi *calipso_doi_getdef(u32 doi) 241a5e34490SHuw Davies { 242a5e34490SHuw Davies struct calipso_doi *doi_def; 243a5e34490SHuw Davies 244a5e34490SHuw Davies rcu_read_lock(); 245a5e34490SHuw Davies doi_def = calipso_doi_search(doi); 246a5e34490SHuw Davies if (!doi_def) 247a5e34490SHuw Davies goto doi_getdef_return; 248a5e34490SHuw Davies if (!atomic_inc_not_zero(&doi_def->refcount)) 249a5e34490SHuw Davies doi_def = NULL; 250a5e34490SHuw Davies 251a5e34490SHuw Davies doi_getdef_return: 252a5e34490SHuw Davies rcu_read_unlock(); 253a5e34490SHuw Davies return doi_def; 254a5e34490SHuw Davies } 255a5e34490SHuw Davies 256a5e34490SHuw Davies /** 257a5e34490SHuw Davies * calipso_doi_putdef - Releases a reference for the given DOI definition 258a5e34490SHuw Davies * @doi_def: the DOI definition 259a5e34490SHuw Davies * 260a5e34490SHuw Davies * Description: 261a5e34490SHuw Davies * Releases a DOI definition reference obtained from calipso_doi_getdef(). 262a5e34490SHuw Davies * 263a5e34490SHuw Davies */ 264a5e34490SHuw Davies static void calipso_doi_putdef(struct calipso_doi *doi_def) 265a5e34490SHuw Davies { 266a5e34490SHuw Davies if (!doi_def) 267a5e34490SHuw Davies return; 268a5e34490SHuw Davies 269a5e34490SHuw Davies if (!atomic_dec_and_test(&doi_def->refcount)) 270a5e34490SHuw Davies return; 271a5e34490SHuw Davies spin_lock(&calipso_doi_list_lock); 272a5e34490SHuw Davies list_del_rcu(&doi_def->list); 273a5e34490SHuw Davies spin_unlock(&calipso_doi_list_lock); 274a5e34490SHuw Davies 275a5e34490SHuw Davies call_rcu(&doi_def->rcu, calipso_doi_free_rcu); 276a5e34490SHuw Davies } 277a5e34490SHuw Davies 278e1ce69dfSHuw Davies /** 279e1ce69dfSHuw Davies * calipso_doi_walk - Iterate through the DOI definitions 280e1ce69dfSHuw Davies * @skip_cnt: skip past this number of DOI definitions, updated 281e1ce69dfSHuw Davies * @callback: callback for each DOI definition 282e1ce69dfSHuw Davies * @cb_arg: argument for the callback function 283e1ce69dfSHuw Davies * 284e1ce69dfSHuw Davies * Description: 285e1ce69dfSHuw Davies * Iterate over the DOI definition list, skipping the first @skip_cnt entries. 286e1ce69dfSHuw Davies * For each entry call @callback, if @callback returns a negative value stop 287e1ce69dfSHuw Davies * 'walking' through the list and return. Updates the value in @skip_cnt upon 288e1ce69dfSHuw Davies * return. Returns zero on success, negative values on failure. 289e1ce69dfSHuw Davies * 290e1ce69dfSHuw Davies */ 291e1ce69dfSHuw Davies static int calipso_doi_walk(u32 *skip_cnt, 292e1ce69dfSHuw Davies int (*callback)(struct calipso_doi *doi_def, 293e1ce69dfSHuw Davies void *arg), 294e1ce69dfSHuw Davies void *cb_arg) 295e1ce69dfSHuw Davies { 296e1ce69dfSHuw Davies int ret_val = -ENOENT; 297e1ce69dfSHuw Davies u32 doi_cnt = 0; 298e1ce69dfSHuw Davies struct calipso_doi *iter_doi; 299e1ce69dfSHuw Davies 300e1ce69dfSHuw Davies rcu_read_lock(); 301e1ce69dfSHuw Davies list_for_each_entry_rcu(iter_doi, &calipso_doi_list, list) 302e1ce69dfSHuw Davies if (atomic_read(&iter_doi->refcount) > 0) { 303e1ce69dfSHuw Davies if (doi_cnt++ < *skip_cnt) 304e1ce69dfSHuw Davies continue; 305e1ce69dfSHuw Davies ret_val = callback(iter_doi, cb_arg); 306e1ce69dfSHuw Davies if (ret_val < 0) { 307e1ce69dfSHuw Davies doi_cnt--; 308e1ce69dfSHuw Davies goto doi_walk_return; 309e1ce69dfSHuw Davies } 310e1ce69dfSHuw Davies } 311e1ce69dfSHuw Davies 312e1ce69dfSHuw Davies doi_walk_return: 313e1ce69dfSHuw Davies rcu_read_unlock(); 314e1ce69dfSHuw Davies *skip_cnt = doi_cnt; 315e1ce69dfSHuw Davies return ret_val; 316e1ce69dfSHuw Davies } 317e1ce69dfSHuw Davies 318*ceba1832SHuw Davies /** 319*ceba1832SHuw Davies * calipso_map_cat_hton - Perform a category mapping from host to network 320*ceba1832SHuw Davies * @doi_def: the DOI definition 321*ceba1832SHuw Davies * @secattr: the security attributes 322*ceba1832SHuw Davies * @net_cat: the zero'd out category bitmap in network/CALIPSO format 323*ceba1832SHuw Davies * @net_cat_len: the length of the CALIPSO bitmap in bytes 324*ceba1832SHuw Davies * 325*ceba1832SHuw Davies * Description: 326*ceba1832SHuw Davies * Perform a label mapping to translate a local MLS category bitmap to the 327*ceba1832SHuw Davies * correct CALIPSO bitmap using the given DOI definition. Returns the minimum 328*ceba1832SHuw Davies * size in bytes of the network bitmap on success, negative values otherwise. 329*ceba1832SHuw Davies * 330*ceba1832SHuw Davies */ 331*ceba1832SHuw Davies static int calipso_map_cat_hton(const struct calipso_doi *doi_def, 332*ceba1832SHuw Davies const struct netlbl_lsm_secattr *secattr, 333*ceba1832SHuw Davies unsigned char *net_cat, 334*ceba1832SHuw Davies u32 net_cat_len) 335*ceba1832SHuw Davies { 336*ceba1832SHuw Davies int spot = -1; 337*ceba1832SHuw Davies u32 net_spot_max = 0; 338*ceba1832SHuw Davies u32 net_clen_bits = net_cat_len * 8; 339*ceba1832SHuw Davies 340*ceba1832SHuw Davies for (;;) { 341*ceba1832SHuw Davies spot = netlbl_catmap_walk(secattr->attr.mls.cat, 342*ceba1832SHuw Davies spot + 1); 343*ceba1832SHuw Davies if (spot < 0) 344*ceba1832SHuw Davies break; 345*ceba1832SHuw Davies if (spot >= net_clen_bits) 346*ceba1832SHuw Davies return -ENOSPC; 347*ceba1832SHuw Davies netlbl_bitmap_setbit(net_cat, spot, 1); 348*ceba1832SHuw Davies 349*ceba1832SHuw Davies if (spot > net_spot_max) 350*ceba1832SHuw Davies net_spot_max = spot; 351*ceba1832SHuw Davies } 352*ceba1832SHuw Davies 353*ceba1832SHuw Davies return (net_spot_max / 32 + 1) * 4; 354*ceba1832SHuw Davies } 355*ceba1832SHuw Davies 356*ceba1832SHuw Davies /** 357*ceba1832SHuw Davies * calipso_map_cat_ntoh - Perform a category mapping from network to host 358*ceba1832SHuw Davies * @doi_def: the DOI definition 359*ceba1832SHuw Davies * @net_cat: the category bitmap in network/CALIPSO format 360*ceba1832SHuw Davies * @net_cat_len: the length of the CALIPSO bitmap in bytes 361*ceba1832SHuw Davies * @secattr: the security attributes 362*ceba1832SHuw Davies * 363*ceba1832SHuw Davies * Description: 364*ceba1832SHuw Davies * Perform a label mapping to translate a CALIPSO bitmap to the correct local 365*ceba1832SHuw Davies * MLS category bitmap using the given DOI definition. Returns zero on 366*ceba1832SHuw Davies * success, negative values on failure. 367*ceba1832SHuw Davies * 368*ceba1832SHuw Davies */ 369*ceba1832SHuw Davies static int calipso_map_cat_ntoh(const struct calipso_doi *doi_def, 370*ceba1832SHuw Davies const unsigned char *net_cat, 371*ceba1832SHuw Davies u32 net_cat_len, 372*ceba1832SHuw Davies struct netlbl_lsm_secattr *secattr) 373*ceba1832SHuw Davies { 374*ceba1832SHuw Davies int ret_val; 375*ceba1832SHuw Davies int spot = -1; 376*ceba1832SHuw Davies u32 net_clen_bits = net_cat_len * 8; 377*ceba1832SHuw Davies 378*ceba1832SHuw Davies for (;;) { 379*ceba1832SHuw Davies spot = netlbl_bitmap_walk(net_cat, 380*ceba1832SHuw Davies net_clen_bits, 381*ceba1832SHuw Davies spot + 1, 382*ceba1832SHuw Davies 1); 383*ceba1832SHuw Davies if (spot < 0) { 384*ceba1832SHuw Davies if (spot == -2) 385*ceba1832SHuw Davies return -EFAULT; 386*ceba1832SHuw Davies return 0; 387*ceba1832SHuw Davies } 388*ceba1832SHuw Davies 389*ceba1832SHuw Davies ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat, 390*ceba1832SHuw Davies spot, 391*ceba1832SHuw Davies GFP_ATOMIC); 392*ceba1832SHuw Davies if (ret_val != 0) 393*ceba1832SHuw Davies return ret_val; 394*ceba1832SHuw Davies } 395*ceba1832SHuw Davies 396*ceba1832SHuw Davies return -EINVAL; 397*ceba1832SHuw Davies } 398*ceba1832SHuw Davies 399*ceba1832SHuw Davies /** 400*ceba1832SHuw Davies * calipso_pad_write - Writes pad bytes in TLV format 401*ceba1832SHuw Davies * @buf: the buffer 402*ceba1832SHuw Davies * @offset: offset from start of buffer to write padding 403*ceba1832SHuw Davies * @count: number of pad bytes to write 404*ceba1832SHuw Davies * 405*ceba1832SHuw Davies * Description: 406*ceba1832SHuw Davies * Write @count bytes of TLV padding into @buffer starting at offset @offset. 407*ceba1832SHuw Davies * @count should be less than 8 - see RFC 4942. 408*ceba1832SHuw Davies * 409*ceba1832SHuw Davies */ 410*ceba1832SHuw Davies static int calipso_pad_write(unsigned char *buf, unsigned int offset, 411*ceba1832SHuw Davies unsigned int count) 412*ceba1832SHuw Davies { 413*ceba1832SHuw Davies if (WARN_ON_ONCE(count >= 8)) 414*ceba1832SHuw Davies return -EINVAL; 415*ceba1832SHuw Davies 416*ceba1832SHuw Davies switch (count) { 417*ceba1832SHuw Davies case 0: 418*ceba1832SHuw Davies break; 419*ceba1832SHuw Davies case 1: 420*ceba1832SHuw Davies buf[offset] = IPV6_TLV_PAD1; 421*ceba1832SHuw Davies break; 422*ceba1832SHuw Davies default: 423*ceba1832SHuw Davies buf[offset] = IPV6_TLV_PADN; 424*ceba1832SHuw Davies buf[offset + 1] = count - 2; 425*ceba1832SHuw Davies if (count > 2) 426*ceba1832SHuw Davies memset(buf + offset + 2, 0, count - 2); 427*ceba1832SHuw Davies break; 428*ceba1832SHuw Davies } 429*ceba1832SHuw Davies return 0; 430*ceba1832SHuw Davies } 431*ceba1832SHuw Davies 432*ceba1832SHuw Davies /** 433*ceba1832SHuw Davies * calipso_genopt - Generate a CALIPSO option 434*ceba1832SHuw Davies * @buf: the option buffer 435*ceba1832SHuw Davies * @start: offset from which to write 436*ceba1832SHuw Davies * @buf_len: the size of opt_buf 437*ceba1832SHuw Davies * @doi_def: the CALIPSO DOI to use 438*ceba1832SHuw Davies * @secattr: the security attributes 439*ceba1832SHuw Davies * 440*ceba1832SHuw Davies * Description: 441*ceba1832SHuw Davies * Generate a CALIPSO option using the DOI definition and security attributes 442*ceba1832SHuw Davies * passed to the function. This also generates upto three bytes of leading 443*ceba1832SHuw Davies * padding that ensures that the option is 4n + 2 aligned. It returns the 444*ceba1832SHuw Davies * number of bytes written (including any initial padding). 445*ceba1832SHuw Davies */ 446*ceba1832SHuw Davies static int calipso_genopt(unsigned char *buf, u32 start, u32 buf_len, 447*ceba1832SHuw Davies const struct calipso_doi *doi_def, 448*ceba1832SHuw Davies const struct netlbl_lsm_secattr *secattr) 449*ceba1832SHuw Davies { 450*ceba1832SHuw Davies int ret_val; 451*ceba1832SHuw Davies u32 len, pad; 452*ceba1832SHuw Davies u16 crc; 453*ceba1832SHuw Davies static const unsigned char padding[4] = {2, 1, 0, 3}; 454*ceba1832SHuw Davies unsigned char *calipso; 455*ceba1832SHuw Davies 456*ceba1832SHuw Davies /* CALIPSO has 4n + 2 alignment */ 457*ceba1832SHuw Davies pad = padding[start & 3]; 458*ceba1832SHuw Davies if (buf_len <= start + pad + CALIPSO_HDR_LEN) 459*ceba1832SHuw Davies return -ENOSPC; 460*ceba1832SHuw Davies 461*ceba1832SHuw Davies if ((secattr->flags & NETLBL_SECATTR_MLS_LVL) == 0) 462*ceba1832SHuw Davies return -EPERM; 463*ceba1832SHuw Davies 464*ceba1832SHuw Davies len = CALIPSO_HDR_LEN; 465*ceba1832SHuw Davies 466*ceba1832SHuw Davies if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { 467*ceba1832SHuw Davies ret_val = calipso_map_cat_hton(doi_def, 468*ceba1832SHuw Davies secattr, 469*ceba1832SHuw Davies buf + start + pad + len, 470*ceba1832SHuw Davies buf_len - start - pad - len); 471*ceba1832SHuw Davies if (ret_val < 0) 472*ceba1832SHuw Davies return ret_val; 473*ceba1832SHuw Davies len += ret_val; 474*ceba1832SHuw Davies } 475*ceba1832SHuw Davies 476*ceba1832SHuw Davies calipso_pad_write(buf, start, pad); 477*ceba1832SHuw Davies calipso = buf + start + pad; 478*ceba1832SHuw Davies 479*ceba1832SHuw Davies calipso[0] = IPV6_TLV_CALIPSO; 480*ceba1832SHuw Davies calipso[1] = len - 2; 481*ceba1832SHuw Davies *(__be32 *)(calipso + 2) = htonl(doi_def->doi); 482*ceba1832SHuw Davies calipso[6] = (len - CALIPSO_HDR_LEN) / 4; 483*ceba1832SHuw Davies calipso[7] = secattr->attr.mls.lvl, 484*ceba1832SHuw Davies crc = ~crc_ccitt(0xffff, calipso, len); 485*ceba1832SHuw Davies calipso[8] = crc & 0xff; 486*ceba1832SHuw Davies calipso[9] = (crc >> 8) & 0xff; 487*ceba1832SHuw Davies return pad + len; 488*ceba1832SHuw Davies } 489*ceba1832SHuw Davies 490*ceba1832SHuw Davies /* Hop-by-hop hdr helper functions 491*ceba1832SHuw Davies */ 492*ceba1832SHuw Davies 493*ceba1832SHuw Davies /** 494*ceba1832SHuw Davies * calipso_opt_update - Replaces socket's hop options with a new set 495*ceba1832SHuw Davies * @sk: the socket 496*ceba1832SHuw Davies * @hop: new hop options 497*ceba1832SHuw Davies * 498*ceba1832SHuw Davies * Description: 499*ceba1832SHuw Davies * Replaces @sk's hop options with @hop. @hop may be NULL to leave 500*ceba1832SHuw Davies * the socket with no hop options. 501*ceba1832SHuw Davies * 502*ceba1832SHuw Davies */ 503*ceba1832SHuw Davies static int calipso_opt_update(struct sock *sk, struct ipv6_opt_hdr *hop) 504*ceba1832SHuw Davies { 505*ceba1832SHuw Davies struct ipv6_txoptions *old = txopt_get(inet6_sk(sk)), *txopts; 506*ceba1832SHuw Davies 507*ceba1832SHuw Davies txopts = ipv6_renew_options_kern(sk, old, IPV6_HOPOPTS, 508*ceba1832SHuw Davies hop, hop ? ipv6_optlen(hop) : 0); 509*ceba1832SHuw Davies txopt_put(old); 510*ceba1832SHuw Davies if (IS_ERR(txopts)) 511*ceba1832SHuw Davies return PTR_ERR(txopts); 512*ceba1832SHuw Davies 513*ceba1832SHuw Davies txopts = ipv6_update_options(sk, txopts); 514*ceba1832SHuw Davies if (txopts) { 515*ceba1832SHuw Davies atomic_sub(txopts->tot_len, &sk->sk_omem_alloc); 516*ceba1832SHuw Davies txopt_put(txopts); 517*ceba1832SHuw Davies } 518*ceba1832SHuw Davies 519*ceba1832SHuw Davies return 0; 520*ceba1832SHuw Davies } 521*ceba1832SHuw Davies 522*ceba1832SHuw Davies /** 523*ceba1832SHuw Davies * calipso_tlv_len - Returns the length of the TLV 524*ceba1832SHuw Davies * @opt: the option header 525*ceba1832SHuw Davies * @offset: offset of the TLV within the header 526*ceba1832SHuw Davies * 527*ceba1832SHuw Davies * Description: 528*ceba1832SHuw Davies * Returns the length of the TLV option at offset @offset within 529*ceba1832SHuw Davies * the option header @opt. Checks that the entire TLV fits inside 530*ceba1832SHuw Davies * the option header, returns a negative value if this is not the case. 531*ceba1832SHuw Davies */ 532*ceba1832SHuw Davies static int calipso_tlv_len(struct ipv6_opt_hdr *opt, unsigned int offset) 533*ceba1832SHuw Davies { 534*ceba1832SHuw Davies unsigned char *tlv = (unsigned char *)opt; 535*ceba1832SHuw Davies unsigned int opt_len = ipv6_optlen(opt), tlv_len; 536*ceba1832SHuw Davies 537*ceba1832SHuw Davies if (offset < sizeof(*opt) || offset >= opt_len) 538*ceba1832SHuw Davies return -EINVAL; 539*ceba1832SHuw Davies if (tlv[offset] == IPV6_TLV_PAD1) 540*ceba1832SHuw Davies return 1; 541*ceba1832SHuw Davies if (offset + 1 >= opt_len) 542*ceba1832SHuw Davies return -EINVAL; 543*ceba1832SHuw Davies tlv_len = tlv[offset + 1] + 2; 544*ceba1832SHuw Davies if (offset + tlv_len > opt_len) 545*ceba1832SHuw Davies return -EINVAL; 546*ceba1832SHuw Davies return tlv_len; 547*ceba1832SHuw Davies } 548*ceba1832SHuw Davies 549*ceba1832SHuw Davies /** 550*ceba1832SHuw Davies * calipso_opt_find - Finds the CALIPSO option in an IPv6 hop options header 551*ceba1832SHuw Davies * @hop: the hop options header 552*ceba1832SHuw Davies * @start: on return holds the offset of any leading padding 553*ceba1832SHuw Davies * @end: on return holds the offset of the first non-pad TLV after CALIPSO 554*ceba1832SHuw Davies * 555*ceba1832SHuw Davies * Description: 556*ceba1832SHuw Davies * Finds the space occupied by a CALIPSO option (including any leading and 557*ceba1832SHuw Davies * trailing padding). 558*ceba1832SHuw Davies * 559*ceba1832SHuw Davies * If a CALIPSO option exists set @start and @end to the 560*ceba1832SHuw Davies * offsets within @hop of the start of padding before the first 561*ceba1832SHuw Davies * CALIPSO option and the end of padding after the first CALIPSO 562*ceba1832SHuw Davies * option. In this case the function returns 0. 563*ceba1832SHuw Davies * 564*ceba1832SHuw Davies * In the absence of a CALIPSO option, @start and @end will be 565*ceba1832SHuw Davies * set to the start and end of any trailing padding in the header. 566*ceba1832SHuw Davies * This is useful when appending a new option, as the caller may want 567*ceba1832SHuw Davies * to overwrite some of this padding. In this case the function will 568*ceba1832SHuw Davies * return -ENOENT. 569*ceba1832SHuw Davies */ 570*ceba1832SHuw Davies static int calipso_opt_find(struct ipv6_opt_hdr *hop, unsigned int *start, 571*ceba1832SHuw Davies unsigned int *end) 572*ceba1832SHuw Davies { 573*ceba1832SHuw Davies int ret_val = -ENOENT, tlv_len; 574*ceba1832SHuw Davies unsigned int opt_len, offset, offset_s = 0, offset_e = 0; 575*ceba1832SHuw Davies unsigned char *opt = (unsigned char *)hop; 576*ceba1832SHuw Davies 577*ceba1832SHuw Davies opt_len = ipv6_optlen(hop); 578*ceba1832SHuw Davies offset = sizeof(*hop); 579*ceba1832SHuw Davies 580*ceba1832SHuw Davies while (offset < opt_len) { 581*ceba1832SHuw Davies tlv_len = calipso_tlv_len(hop, offset); 582*ceba1832SHuw Davies if (tlv_len < 0) 583*ceba1832SHuw Davies return tlv_len; 584*ceba1832SHuw Davies 585*ceba1832SHuw Davies switch (opt[offset]) { 586*ceba1832SHuw Davies case IPV6_TLV_PAD1: 587*ceba1832SHuw Davies case IPV6_TLV_PADN: 588*ceba1832SHuw Davies if (offset_e) 589*ceba1832SHuw Davies offset_e = offset; 590*ceba1832SHuw Davies break; 591*ceba1832SHuw Davies case IPV6_TLV_CALIPSO: 592*ceba1832SHuw Davies ret_val = 0; 593*ceba1832SHuw Davies offset_e = offset; 594*ceba1832SHuw Davies break; 595*ceba1832SHuw Davies default: 596*ceba1832SHuw Davies if (offset_e == 0) 597*ceba1832SHuw Davies offset_s = offset; 598*ceba1832SHuw Davies else 599*ceba1832SHuw Davies goto out; 600*ceba1832SHuw Davies } 601*ceba1832SHuw Davies offset += tlv_len; 602*ceba1832SHuw Davies } 603*ceba1832SHuw Davies 604*ceba1832SHuw Davies out: 605*ceba1832SHuw Davies if (offset_s) 606*ceba1832SHuw Davies *start = offset_s + calipso_tlv_len(hop, offset_s); 607*ceba1832SHuw Davies else 608*ceba1832SHuw Davies *start = sizeof(*hop); 609*ceba1832SHuw Davies if (offset_e) 610*ceba1832SHuw Davies *end = offset_e + calipso_tlv_len(hop, offset_e); 611*ceba1832SHuw Davies else 612*ceba1832SHuw Davies *end = opt_len; 613*ceba1832SHuw Davies 614*ceba1832SHuw Davies return ret_val; 615*ceba1832SHuw Davies } 616*ceba1832SHuw Davies 617*ceba1832SHuw Davies /** 618*ceba1832SHuw Davies * calipso_opt_insert - Inserts a CALIPSO option into an IPv6 hop opt hdr 619*ceba1832SHuw Davies * @hop: the original hop options header 620*ceba1832SHuw Davies * @doi_def: the CALIPSO DOI to use 621*ceba1832SHuw Davies * @secattr: the specific security attributes of the socket 622*ceba1832SHuw Davies * 623*ceba1832SHuw Davies * Description: 624*ceba1832SHuw Davies * Creates a new hop options header based on @hop with a 625*ceba1832SHuw Davies * CALIPSO option added to it. If @hop already contains a CALIPSO 626*ceba1832SHuw Davies * option this is overwritten, otherwise the new option is appended 627*ceba1832SHuw Davies * after any existing options. If @hop is NULL then the new header 628*ceba1832SHuw Davies * will contain just the CALIPSO option and any needed padding. 629*ceba1832SHuw Davies * 630*ceba1832SHuw Davies */ 631*ceba1832SHuw Davies static struct ipv6_opt_hdr * 632*ceba1832SHuw Davies calipso_opt_insert(struct ipv6_opt_hdr *hop, 633*ceba1832SHuw Davies const struct calipso_doi *doi_def, 634*ceba1832SHuw Davies const struct netlbl_lsm_secattr *secattr) 635*ceba1832SHuw Davies { 636*ceba1832SHuw Davies unsigned int start, end, buf_len, pad, hop_len; 637*ceba1832SHuw Davies struct ipv6_opt_hdr *new; 638*ceba1832SHuw Davies int ret_val; 639*ceba1832SHuw Davies 640*ceba1832SHuw Davies if (hop) { 641*ceba1832SHuw Davies hop_len = ipv6_optlen(hop); 642*ceba1832SHuw Davies ret_val = calipso_opt_find(hop, &start, &end); 643*ceba1832SHuw Davies if (ret_val && ret_val != -ENOENT) 644*ceba1832SHuw Davies return ERR_PTR(ret_val); 645*ceba1832SHuw Davies } else { 646*ceba1832SHuw Davies hop_len = 0; 647*ceba1832SHuw Davies start = sizeof(*hop); 648*ceba1832SHuw Davies end = 0; 649*ceba1832SHuw Davies } 650*ceba1832SHuw Davies 651*ceba1832SHuw Davies buf_len = hop_len + start - end + CALIPSO_OPT_LEN_MAX_WITH_PAD; 652*ceba1832SHuw Davies new = kzalloc(buf_len, GFP_ATOMIC); 653*ceba1832SHuw Davies if (!new) 654*ceba1832SHuw Davies return ERR_PTR(-ENOMEM); 655*ceba1832SHuw Davies 656*ceba1832SHuw Davies if (start > sizeof(*hop)) 657*ceba1832SHuw Davies memcpy(new, hop, start); 658*ceba1832SHuw Davies ret_val = calipso_genopt((unsigned char *)new, start, buf_len, doi_def, 659*ceba1832SHuw Davies secattr); 660*ceba1832SHuw Davies if (ret_val < 0) 661*ceba1832SHuw Davies return ERR_PTR(ret_val); 662*ceba1832SHuw Davies 663*ceba1832SHuw Davies buf_len = start + ret_val; 664*ceba1832SHuw Davies /* At this point buf_len aligns to 4n, so (buf_len & 4) pads to 8n */ 665*ceba1832SHuw Davies pad = ((buf_len & 4) + (end & 7)) & 7; 666*ceba1832SHuw Davies calipso_pad_write((unsigned char *)new, buf_len, pad); 667*ceba1832SHuw Davies buf_len += pad; 668*ceba1832SHuw Davies 669*ceba1832SHuw Davies if (end != hop_len) { 670*ceba1832SHuw Davies memcpy((char *)new + buf_len, (char *)hop + end, hop_len - end); 671*ceba1832SHuw Davies buf_len += hop_len - end; 672*ceba1832SHuw Davies } 673*ceba1832SHuw Davies new->nexthdr = 0; 674*ceba1832SHuw Davies new->hdrlen = buf_len / 8 - 1; 675*ceba1832SHuw Davies 676*ceba1832SHuw Davies return new; 677*ceba1832SHuw Davies } 678*ceba1832SHuw Davies 679*ceba1832SHuw Davies /** 680*ceba1832SHuw Davies * calipso_opt_del - Removes the CALIPSO option from an option header 681*ceba1832SHuw Davies * @hop: the original header 682*ceba1832SHuw Davies * @new: the new header 683*ceba1832SHuw Davies * 684*ceba1832SHuw Davies * Description: 685*ceba1832SHuw Davies * Creates a new header based on @hop without any CALIPSO option. If @hop 686*ceba1832SHuw Davies * doesn't contain a CALIPSO option it returns -ENOENT. If @hop contains 687*ceba1832SHuw Davies * no other non-padding options, it returns zero with @new set to NULL. 688*ceba1832SHuw Davies * Otherwise it returns zero, creates a new header without the CALIPSO 689*ceba1832SHuw Davies * option (and removing as much padding as possible) and returns with 690*ceba1832SHuw Davies * @new set to that header. 691*ceba1832SHuw Davies * 692*ceba1832SHuw Davies */ 693*ceba1832SHuw Davies static int calipso_opt_del(struct ipv6_opt_hdr *hop, 694*ceba1832SHuw Davies struct ipv6_opt_hdr **new) 695*ceba1832SHuw Davies { 696*ceba1832SHuw Davies int ret_val; 697*ceba1832SHuw Davies unsigned int start, end, delta, pad, hop_len; 698*ceba1832SHuw Davies 699*ceba1832SHuw Davies ret_val = calipso_opt_find(hop, &start, &end); 700*ceba1832SHuw Davies if (ret_val) 701*ceba1832SHuw Davies return ret_val; 702*ceba1832SHuw Davies 703*ceba1832SHuw Davies hop_len = ipv6_optlen(hop); 704*ceba1832SHuw Davies if (start == sizeof(*hop) && end == hop_len) { 705*ceba1832SHuw Davies /* There's no other option in the header so return NULL */ 706*ceba1832SHuw Davies *new = NULL; 707*ceba1832SHuw Davies return 0; 708*ceba1832SHuw Davies } 709*ceba1832SHuw Davies 710*ceba1832SHuw Davies delta = (end - start) & ~7; 711*ceba1832SHuw Davies *new = kzalloc(hop_len - delta, GFP_ATOMIC); 712*ceba1832SHuw Davies if (!*new) 713*ceba1832SHuw Davies return -ENOMEM; 714*ceba1832SHuw Davies 715*ceba1832SHuw Davies memcpy(*new, hop, start); 716*ceba1832SHuw Davies (*new)->hdrlen -= delta / 8; 717*ceba1832SHuw Davies pad = (end - start) & 7; 718*ceba1832SHuw Davies calipso_pad_write((unsigned char *)*new, start, pad); 719*ceba1832SHuw Davies if (end != hop_len) 720*ceba1832SHuw Davies memcpy((char *)*new + start + pad, (char *)hop + end, 721*ceba1832SHuw Davies hop_len - end); 722*ceba1832SHuw Davies 723*ceba1832SHuw Davies return 0; 724*ceba1832SHuw Davies } 725*ceba1832SHuw Davies 726*ceba1832SHuw Davies /** 727*ceba1832SHuw Davies * calipso_opt_getattr - Get the security attributes from a memory block 728*ceba1832SHuw Davies * @calipso: the CALIPSO option 729*ceba1832SHuw Davies * @secattr: the security attributes 730*ceba1832SHuw Davies * 731*ceba1832SHuw Davies * Description: 732*ceba1832SHuw Davies * Inspect @calipso and return the security attributes in @secattr. 733*ceba1832SHuw Davies * Returns zero on success and negative values on failure. 734*ceba1832SHuw Davies * 735*ceba1832SHuw Davies */ 736*ceba1832SHuw Davies static int calipso_opt_getattr(const unsigned char *calipso, 737*ceba1832SHuw Davies struct netlbl_lsm_secattr *secattr) 738*ceba1832SHuw Davies { 739*ceba1832SHuw Davies int ret_val = -ENOMSG; 740*ceba1832SHuw Davies u32 doi, len = calipso[1], cat_len = calipso[6] * 4; 741*ceba1832SHuw Davies struct calipso_doi *doi_def; 742*ceba1832SHuw Davies 743*ceba1832SHuw Davies if (cat_len + 8 > len) 744*ceba1832SHuw Davies return -EINVAL; 745*ceba1832SHuw Davies 746*ceba1832SHuw Davies doi = get_unaligned_be32(calipso + 2); 747*ceba1832SHuw Davies rcu_read_lock(); 748*ceba1832SHuw Davies doi_def = calipso_doi_search(doi); 749*ceba1832SHuw Davies if (!doi_def) 750*ceba1832SHuw Davies goto getattr_return; 751*ceba1832SHuw Davies 752*ceba1832SHuw Davies secattr->attr.mls.lvl = calipso[7]; 753*ceba1832SHuw Davies secattr->flags |= NETLBL_SECATTR_MLS_LVL; 754*ceba1832SHuw Davies 755*ceba1832SHuw Davies if (cat_len) { 756*ceba1832SHuw Davies ret_val = calipso_map_cat_ntoh(doi_def, 757*ceba1832SHuw Davies calipso + 10, 758*ceba1832SHuw Davies cat_len, 759*ceba1832SHuw Davies secattr); 760*ceba1832SHuw Davies if (ret_val != 0) { 761*ceba1832SHuw Davies netlbl_catmap_free(secattr->attr.mls.cat); 762*ceba1832SHuw Davies goto getattr_return; 763*ceba1832SHuw Davies } 764*ceba1832SHuw Davies 765*ceba1832SHuw Davies secattr->flags |= NETLBL_SECATTR_MLS_CAT; 766*ceba1832SHuw Davies } 767*ceba1832SHuw Davies 768*ceba1832SHuw Davies secattr->type = NETLBL_NLTYPE_CALIPSO; 769*ceba1832SHuw Davies 770*ceba1832SHuw Davies getattr_return: 771*ceba1832SHuw Davies rcu_read_unlock(); 772*ceba1832SHuw Davies return ret_val; 773*ceba1832SHuw Davies } 774*ceba1832SHuw Davies 775*ceba1832SHuw Davies /* sock functions. 776*ceba1832SHuw Davies */ 777*ceba1832SHuw Davies 778*ceba1832SHuw Davies /** 779*ceba1832SHuw Davies * calipso_sock_getattr - Get the security attributes from a sock 780*ceba1832SHuw Davies * @sk: the sock 781*ceba1832SHuw Davies * @secattr: the security attributes 782*ceba1832SHuw Davies * 783*ceba1832SHuw Davies * Description: 784*ceba1832SHuw Davies * Query @sk to see if there is a CALIPSO option attached to the sock and if 785*ceba1832SHuw Davies * there is return the CALIPSO security attributes in @secattr. This function 786*ceba1832SHuw Davies * requires that @sk be locked, or privately held, but it does not do any 787*ceba1832SHuw Davies * locking itself. Returns zero on success and negative values on failure. 788*ceba1832SHuw Davies * 789*ceba1832SHuw Davies */ 790*ceba1832SHuw Davies static int calipso_sock_getattr(struct sock *sk, 791*ceba1832SHuw Davies struct netlbl_lsm_secattr *secattr) 792*ceba1832SHuw Davies { 793*ceba1832SHuw Davies struct ipv6_opt_hdr *hop; 794*ceba1832SHuw Davies int opt_len, len, ret_val = -ENOMSG, offset; 795*ceba1832SHuw Davies unsigned char *opt; 796*ceba1832SHuw Davies struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk)); 797*ceba1832SHuw Davies 798*ceba1832SHuw Davies if (!txopts || !txopts->hopopt) 799*ceba1832SHuw Davies goto done; 800*ceba1832SHuw Davies 801*ceba1832SHuw Davies hop = txopts->hopopt; 802*ceba1832SHuw Davies opt = (unsigned char *)hop; 803*ceba1832SHuw Davies opt_len = ipv6_optlen(hop); 804*ceba1832SHuw Davies offset = sizeof(*hop); 805*ceba1832SHuw Davies while (offset < opt_len) { 806*ceba1832SHuw Davies len = calipso_tlv_len(hop, offset); 807*ceba1832SHuw Davies if (len < 0) { 808*ceba1832SHuw Davies ret_val = len; 809*ceba1832SHuw Davies goto done; 810*ceba1832SHuw Davies } 811*ceba1832SHuw Davies switch (opt[offset]) { 812*ceba1832SHuw Davies case IPV6_TLV_CALIPSO: 813*ceba1832SHuw Davies if (len < CALIPSO_HDR_LEN) 814*ceba1832SHuw Davies ret_val = -EINVAL; 815*ceba1832SHuw Davies else 816*ceba1832SHuw Davies ret_val = calipso_opt_getattr(&opt[offset], 817*ceba1832SHuw Davies secattr); 818*ceba1832SHuw Davies goto done; 819*ceba1832SHuw Davies default: 820*ceba1832SHuw Davies offset += len; 821*ceba1832SHuw Davies break; 822*ceba1832SHuw Davies } 823*ceba1832SHuw Davies } 824*ceba1832SHuw Davies done: 825*ceba1832SHuw Davies txopt_put(txopts); 826*ceba1832SHuw Davies return ret_val; 827*ceba1832SHuw Davies } 828*ceba1832SHuw Davies 829*ceba1832SHuw Davies /** 830*ceba1832SHuw Davies * calipso_sock_setattr - Add a CALIPSO option to a socket 831*ceba1832SHuw Davies * @sk: the socket 832*ceba1832SHuw Davies * @doi_def: the CALIPSO DOI to use 833*ceba1832SHuw Davies * @secattr: the specific security attributes of the socket 834*ceba1832SHuw Davies * 835*ceba1832SHuw Davies * Description: 836*ceba1832SHuw Davies * Set the CALIPSO option on the given socket using the DOI definition and 837*ceba1832SHuw Davies * security attributes passed to the function. This function requires 838*ceba1832SHuw Davies * exclusive access to @sk, which means it either needs to be in the 839*ceba1832SHuw Davies * process of being created or locked. Returns zero on success and negative 840*ceba1832SHuw Davies * values on failure. 841*ceba1832SHuw Davies * 842*ceba1832SHuw Davies */ 843*ceba1832SHuw Davies static int calipso_sock_setattr(struct sock *sk, 844*ceba1832SHuw Davies const struct calipso_doi *doi_def, 845*ceba1832SHuw Davies const struct netlbl_lsm_secattr *secattr) 846*ceba1832SHuw Davies { 847*ceba1832SHuw Davies int ret_val; 848*ceba1832SHuw Davies struct ipv6_opt_hdr *old, *new; 849*ceba1832SHuw Davies struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk)); 850*ceba1832SHuw Davies 851*ceba1832SHuw Davies old = NULL; 852*ceba1832SHuw Davies if (txopts) 853*ceba1832SHuw Davies old = txopts->hopopt; 854*ceba1832SHuw Davies 855*ceba1832SHuw Davies new = calipso_opt_insert(old, doi_def, secattr); 856*ceba1832SHuw Davies txopt_put(txopts); 857*ceba1832SHuw Davies if (IS_ERR(new)) 858*ceba1832SHuw Davies return PTR_ERR(new); 859*ceba1832SHuw Davies 860*ceba1832SHuw Davies ret_val = calipso_opt_update(sk, new); 861*ceba1832SHuw Davies 862*ceba1832SHuw Davies kfree(new); 863*ceba1832SHuw Davies return ret_val; 864*ceba1832SHuw Davies } 865*ceba1832SHuw Davies 866*ceba1832SHuw Davies /** 867*ceba1832SHuw Davies * calipso_sock_delattr - Delete the CALIPSO option from a socket 868*ceba1832SHuw Davies * @sk: the socket 869*ceba1832SHuw Davies * 870*ceba1832SHuw Davies * Description: 871*ceba1832SHuw Davies * Removes the CALIPSO option from a socket, if present. 872*ceba1832SHuw Davies * 873*ceba1832SHuw Davies */ 874*ceba1832SHuw Davies static void calipso_sock_delattr(struct sock *sk) 875*ceba1832SHuw Davies { 876*ceba1832SHuw Davies struct ipv6_opt_hdr *new_hop; 877*ceba1832SHuw Davies struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk)); 878*ceba1832SHuw Davies 879*ceba1832SHuw Davies if (!txopts || !txopts->hopopt) 880*ceba1832SHuw Davies goto done; 881*ceba1832SHuw Davies 882*ceba1832SHuw Davies if (calipso_opt_del(txopts->hopopt, &new_hop)) 883*ceba1832SHuw Davies goto done; 884*ceba1832SHuw Davies 885*ceba1832SHuw Davies calipso_opt_update(sk, new_hop); 886*ceba1832SHuw Davies kfree(new_hop); 887*ceba1832SHuw Davies 888*ceba1832SHuw Davies done: 889*ceba1832SHuw Davies txopt_put(txopts); 890*ceba1832SHuw Davies } 891*ceba1832SHuw Davies 892cb72d382SHuw Davies static const struct netlbl_calipso_ops ops = { 893cb72d382SHuw Davies .doi_add = calipso_doi_add, 894cb72d382SHuw Davies .doi_free = calipso_doi_free, 895d7cce015SHuw Davies .doi_remove = calipso_doi_remove, 896a5e34490SHuw Davies .doi_getdef = calipso_doi_getdef, 897a5e34490SHuw Davies .doi_putdef = calipso_doi_putdef, 898e1ce69dfSHuw Davies .doi_walk = calipso_doi_walk, 899*ceba1832SHuw Davies .sock_getattr = calipso_sock_getattr, 900*ceba1832SHuw Davies .sock_setattr = calipso_sock_setattr, 901*ceba1832SHuw Davies .sock_delattr = calipso_sock_delattr, 902cb72d382SHuw Davies }; 903cb72d382SHuw Davies 904cb72d382SHuw Davies /** 905cb72d382SHuw Davies * calipso_init - Initialize the CALIPSO module 906cb72d382SHuw Davies * 907cb72d382SHuw Davies * Description: 908cb72d382SHuw Davies * Initialize the CALIPSO module and prepare it for use. Returns zero on 909cb72d382SHuw Davies * success and negative values on failure. 910cb72d382SHuw Davies * 911cb72d382SHuw Davies */ 912cb72d382SHuw Davies int __init calipso_init(void) 913cb72d382SHuw Davies { 914cb72d382SHuw Davies netlbl_calipso_ops_register(&ops); 915cb72d382SHuw Davies return 0; 916cb72d382SHuw Davies } 917cb72d382SHuw Davies 918cb72d382SHuw Davies void calipso_exit(void) 919cb72d382SHuw Davies { 920cb72d382SHuw Davies netlbl_calipso_ops_register(NULL); 921cb72d382SHuw Davies } 922