diff options
author | Paul Moore <paul.moore@hp.com> | 2009-03-27 17:10:54 -0400 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2009-03-28 00:01:37 -0400 |
commit | 07feee8f812f7327a46186f7604df312c8c81962 (patch) | |
tree | 73eac643b60532aa82d7680a7de193ba2b62eddd /security/smack/smack_lsm.c | |
parent | 8651d5c0b1f874c5b8307ae2b858bc40f9f02482 (diff) |
netlabel: Cleanup the Smack/NetLabel code to fix incoming TCP connections
This patch cleans up a lot of the Smack network access control code. The
largest changes are to fix the labeling of incoming TCP connections in a
manner similar to the recent SELinux changes which use the
security_inet_conn_request() hook to label the request_sock and let the label
move to the child socket via the normal network stack mechanisms. In addition
to the incoming TCP connection fixes this patch also removes the smk_labled
field from the socket_smack struct as the minor optimization advantage was
outweighed by the difficulty in maintaining it's proper state.
Signed-off-by: Paul Moore <paul.moore@hp.com>
Acked-by: Casey Schaufler <casey@schaufler-ca.com>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/smack/smack_lsm.c')
-rw-r--r-- | security/smack/smack_lsm.c | 260 |
1 files changed, 143 insertions, 117 deletions
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 23ad420a49aa..8ed502c2ad45 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c | |||
@@ -7,6 +7,8 @@ | |||
7 | * Casey Schaufler <casey@schaufler-ca.com> | 7 | * Casey Schaufler <casey@schaufler-ca.com> |
8 | * | 8 | * |
9 | * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com> | 9 | * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com> |
10 | * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. | ||
11 | * Paul Moore <paul.moore@hp.com> | ||
10 | * | 12 | * |
11 | * This program is free software; you can redistribute it and/or modify | 13 | * This program is free software; you can redistribute it and/or modify |
12 | * it under the terms of the GNU General Public License version 2, | 14 | * it under the terms of the GNU General Public License version 2, |
@@ -20,6 +22,7 @@ | |||
20 | #include <linux/ext2_fs.h> | 22 | #include <linux/ext2_fs.h> |
21 | #include <linux/kd.h> | 23 | #include <linux/kd.h> |
22 | #include <asm/ioctls.h> | 24 | #include <asm/ioctls.h> |
25 | #include <linux/ip.h> | ||
23 | #include <linux/tcp.h> | 26 | #include <linux/tcp.h> |
24 | #include <linux/udp.h> | 27 | #include <linux/udp.h> |
25 | #include <linux/mutex.h> | 28 | #include <linux/mutex.h> |
@@ -1275,7 +1278,6 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) | |||
1275 | 1278 | ||
1276 | ssp->smk_in = csp; | 1279 | ssp->smk_in = csp; |
1277 | ssp->smk_out = csp; | 1280 | ssp->smk_out = csp; |
1278 | ssp->smk_labeled = SMACK_CIPSO_SOCKET; | ||
1279 | ssp->smk_packet[0] = '\0'; | 1281 | ssp->smk_packet[0] = '\0'; |
1280 | 1282 | ||
1281 | sk->sk_security = ssp; | 1283 | sk->sk_security = ssp; |
@@ -1295,6 +1297,39 @@ static void smack_sk_free_security(struct sock *sk) | |||
1295 | } | 1297 | } |
1296 | 1298 | ||
1297 | /** | 1299 | /** |
1300 | * smack_host_label - check host based restrictions | ||
1301 | * @sip: the object end | ||
1302 | * | ||
1303 | * looks for host based access restrictions | ||
1304 | * | ||
1305 | * This version will only be appropriate for really small sets of single label | ||
1306 | * hosts. The caller is responsible for ensuring that the RCU read lock is | ||
1307 | * taken before calling this function. | ||
1308 | * | ||
1309 | * Returns the label of the far end or NULL if it's not special. | ||
1310 | */ | ||
1311 | static char *smack_host_label(struct sockaddr_in *sip) | ||
1312 | { | ||
1313 | struct smk_netlbladdr *snp; | ||
1314 | struct in_addr *siap = &sip->sin_addr; | ||
1315 | |||
1316 | if (siap->s_addr == 0) | ||
1317 | return NULL; | ||
1318 | |||
1319 | list_for_each_entry_rcu(snp, &smk_netlbladdr_list, list) | ||
1320 | /* | ||
1321 | * we break after finding the first match because | ||
1322 | * the list is sorted from longest to shortest mask | ||
1323 | * so we have found the most specific match | ||
1324 | */ | ||
1325 | if ((&snp->smk_host.sin_addr)->s_addr == | ||
1326 | (siap->s_addr & (&snp->smk_mask)->s_addr)) | ||
1327 | return snp->smk_label; | ||
1328 | |||
1329 | return NULL; | ||
1330 | } | ||
1331 | |||
1332 | /** | ||
1298 | * smack_set_catset - convert a capset to netlabel mls categories | 1333 | * smack_set_catset - convert a capset to netlabel mls categories |
1299 | * @catset: the Smack categories | 1334 | * @catset: the Smack categories |
1300 | * @sap: where to put the netlabel categories | 1335 | * @sap: where to put the netlabel categories |
@@ -1365,11 +1400,10 @@ static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp) | |||
1365 | */ | 1400 | */ |
1366 | static int smack_netlabel(struct sock *sk, int labeled) | 1401 | static int smack_netlabel(struct sock *sk, int labeled) |
1367 | { | 1402 | { |
1368 | struct socket_smack *ssp; | 1403 | struct socket_smack *ssp = sk->sk_security; |
1369 | struct netlbl_lsm_secattr secattr; | 1404 | struct netlbl_lsm_secattr secattr; |
1370 | int rc = 0; | 1405 | int rc = 0; |
1371 | 1406 | ||
1372 | ssp = sk->sk_security; | ||
1373 | /* | 1407 | /* |
1374 | * Usually the netlabel code will handle changing the | 1408 | * Usually the netlabel code will handle changing the |
1375 | * packet labeling based on the label. | 1409 | * packet labeling based on the label. |
@@ -1393,21 +1427,45 @@ static int smack_netlabel(struct sock *sk, int labeled) | |||
1393 | 1427 | ||
1394 | bh_unlock_sock(sk); | 1428 | bh_unlock_sock(sk); |
1395 | local_bh_enable(); | 1429 | local_bh_enable(); |
1396 | /* | ||
1397 | * Remember the label scheme used so that it is not | ||
1398 | * necessary to do the netlabel setting if it has not | ||
1399 | * changed the next time through. | ||
1400 | * | ||
1401 | * The -EDESTADDRREQ case is an indication that there's | ||
1402 | * a single level host involved. | ||
1403 | */ | ||
1404 | if (rc == 0) | ||
1405 | ssp->smk_labeled = labeled; | ||
1406 | 1430 | ||
1407 | return rc; | 1431 | return rc; |
1408 | } | 1432 | } |
1409 | 1433 | ||
1410 | /** | 1434 | /** |
1435 | * smack_netlbel_send - Set the secattr on a socket and perform access checks | ||
1436 | * @sk: the socket | ||
1437 | * @sap: the destination address | ||
1438 | * | ||
1439 | * Set the correct secattr for the given socket based on the destination | ||
1440 | * address and perform any outbound access checks needed. | ||
1441 | * | ||
1442 | * Returns 0 on success or an error code. | ||
1443 | * | ||
1444 | */ | ||
1445 | static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) | ||
1446 | { | ||
1447 | int rc; | ||
1448 | int sk_lbl; | ||
1449 | char *hostsp; | ||
1450 | struct socket_smack *ssp = sk->sk_security; | ||
1451 | |||
1452 | rcu_read_lock(); | ||
1453 | hostsp = smack_host_label(sap); | ||
1454 | if (hostsp != NULL) { | ||
1455 | sk_lbl = SMACK_UNLABELED_SOCKET; | ||
1456 | rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE); | ||
1457 | } else { | ||
1458 | sk_lbl = SMACK_CIPSO_SOCKET; | ||
1459 | rc = 0; | ||
1460 | } | ||
1461 | rcu_read_unlock(); | ||
1462 | if (rc != 0) | ||
1463 | return rc; | ||
1464 | |||
1465 | return smack_netlabel(sk, sk_lbl); | ||
1466 | } | ||
1467 | |||
1468 | /** | ||
1411 | * smack_inode_setsecurity - set smack xattrs | 1469 | * smack_inode_setsecurity - set smack xattrs |
1412 | * @inode: the object | 1470 | * @inode: the object |
1413 | * @name: attribute name | 1471 | * @name: attribute name |
@@ -1488,43 +1546,6 @@ static int smack_socket_post_create(struct socket *sock, int family, | |||
1488 | return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); | 1546 | return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); |
1489 | } | 1547 | } |
1490 | 1548 | ||
1491 | |||
1492 | /** | ||
1493 | * smack_host_label - check host based restrictions | ||
1494 | * @sip: the object end | ||
1495 | * | ||
1496 | * looks for host based access restrictions | ||
1497 | * | ||
1498 | * This version will only be appropriate for really small | ||
1499 | * sets of single label hosts. | ||
1500 | * | ||
1501 | * Returns the label of the far end or NULL if it's not special. | ||
1502 | */ | ||
1503 | static char *smack_host_label(struct sockaddr_in *sip) | ||
1504 | { | ||
1505 | struct smk_netlbladdr *snp; | ||
1506 | struct in_addr *siap = &sip->sin_addr; | ||
1507 | |||
1508 | if (siap->s_addr == 0) | ||
1509 | return NULL; | ||
1510 | |||
1511 | rcu_read_lock(); | ||
1512 | list_for_each_entry_rcu(snp, &smk_netlbladdr_list, list) { | ||
1513 | /* | ||
1514 | * we break after finding the first match because | ||
1515 | * the list is sorted from longest to shortest mask | ||
1516 | * so we have found the most specific match | ||
1517 | */ | ||
1518 | if ((&snp->smk_host.sin_addr)->s_addr == | ||
1519 | (siap->s_addr & (&snp->smk_mask)->s_addr)) { | ||
1520 | rcu_read_unlock(); | ||
1521 | return snp->smk_label; | ||
1522 | } | ||
1523 | } | ||
1524 | rcu_read_unlock(); | ||
1525 | return NULL; | ||
1526 | } | ||
1527 | |||
1528 | /** | 1549 | /** |
1529 | * smack_socket_connect - connect access check | 1550 | * smack_socket_connect - connect access check |
1530 | * @sock: the socket | 1551 | * @sock: the socket |
@@ -1538,30 +1559,12 @@ static char *smack_host_label(struct sockaddr_in *sip) | |||
1538 | static int smack_socket_connect(struct socket *sock, struct sockaddr *sap, | 1559 | static int smack_socket_connect(struct socket *sock, struct sockaddr *sap, |
1539 | int addrlen) | 1560 | int addrlen) |
1540 | { | 1561 | { |
1541 | struct socket_smack *ssp = sock->sk->sk_security; | ||
1542 | char *hostsp; | ||
1543 | int rc; | ||
1544 | |||
1545 | if (sock->sk == NULL || sock->sk->sk_family != PF_INET) | 1562 | if (sock->sk == NULL || sock->sk->sk_family != PF_INET) |
1546 | return 0; | 1563 | return 0; |
1547 | |||
1548 | if (addrlen < sizeof(struct sockaddr_in)) | 1564 | if (addrlen < sizeof(struct sockaddr_in)) |
1549 | return -EINVAL; | 1565 | return -EINVAL; |
1550 | 1566 | ||
1551 | hostsp = smack_host_label((struct sockaddr_in *)sap); | 1567 | return smack_netlabel_send(sock->sk, (struct sockaddr_in *)sap); |
1552 | if (hostsp == NULL) { | ||
1553 | if (ssp->smk_labeled != SMACK_CIPSO_SOCKET) | ||
1554 | return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); | ||
1555 | return 0; | ||
1556 | } | ||
1557 | |||
1558 | rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE); | ||
1559 | if (rc != 0) | ||
1560 | return rc; | ||
1561 | |||
1562 | if (ssp->smk_labeled != SMACK_UNLABELED_SOCKET) | ||
1563 | return smack_netlabel(sock->sk, SMACK_UNLABELED_SOCKET); | ||
1564 | return 0; | ||
1565 | } | 1568 | } |
1566 | 1569 | ||
1567 | /** | 1570 | /** |
@@ -2262,9 +2265,6 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, | |||
2262 | int size) | 2265 | int size) |
2263 | { | 2266 | { |
2264 | struct sockaddr_in *sip = (struct sockaddr_in *) msg->msg_name; | 2267 | struct sockaddr_in *sip = (struct sockaddr_in *) msg->msg_name; |
2265 | struct socket_smack *ssp = sock->sk->sk_security; | ||
2266 | char *hostsp; | ||
2267 | int rc; | ||
2268 | 2268 | ||
2269 | /* | 2269 | /* |
2270 | * Perfectly reasonable for this to be NULL | 2270 | * Perfectly reasonable for this to be NULL |
@@ -2272,22 +2272,7 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, | |||
2272 | if (sip == NULL || sip->sin_family != PF_INET) | 2272 | if (sip == NULL || sip->sin_family != PF_INET) |
2273 | return 0; | 2273 | return 0; |
2274 | 2274 | ||
2275 | hostsp = smack_host_label(sip); | 2275 | return smack_netlabel_send(sock->sk, sip); |
2276 | if (hostsp == NULL) { | ||
2277 | if (ssp->smk_labeled != SMACK_CIPSO_SOCKET) | ||
2278 | return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); | ||
2279 | return 0; | ||
2280 | } | ||
2281 | |||
2282 | rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE); | ||
2283 | if (rc != 0) | ||
2284 | return rc; | ||
2285 | |||
2286 | if (ssp->smk_labeled != SMACK_UNLABELED_SOCKET) | ||
2287 | return smack_netlabel(sock->sk, SMACK_UNLABELED_SOCKET); | ||
2288 | |||
2289 | return 0; | ||
2290 | |||
2291 | } | 2276 | } |
2292 | 2277 | ||
2293 | 2278 | ||
@@ -2492,31 +2477,24 @@ static int smack_socket_getpeersec_dgram(struct socket *sock, | |||
2492 | } | 2477 | } |
2493 | 2478 | ||
2494 | /** | 2479 | /** |
2495 | * smack_sock_graft - graft access state between two sockets | 2480 | * smack_sock_graft - Initialize a newly created socket with an existing sock |
2496 | * @sk: fresh sock | 2481 | * @sk: child sock |
2497 | * @parent: donor socket | 2482 | * @parent: parent socket |
2498 | * | 2483 | * |
2499 | * Sets the netlabel socket state on sk from parent | 2484 | * Set the smk_{in,out} state of an existing sock based on the process that |
2485 | * is creating the new socket. | ||
2500 | */ | 2486 | */ |
2501 | static void smack_sock_graft(struct sock *sk, struct socket *parent) | 2487 | static void smack_sock_graft(struct sock *sk, struct socket *parent) |
2502 | { | 2488 | { |
2503 | struct socket_smack *ssp; | 2489 | struct socket_smack *ssp; |
2504 | int rc; | ||
2505 | |||
2506 | if (sk == NULL) | ||
2507 | return; | ||
2508 | 2490 | ||
2509 | if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6) | 2491 | if (sk == NULL || |
2492 | (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)) | ||
2510 | return; | 2493 | return; |
2511 | 2494 | ||
2512 | ssp = sk->sk_security; | 2495 | ssp = sk->sk_security; |
2513 | ssp->smk_in = ssp->smk_out = current_security(); | 2496 | ssp->smk_in = ssp->smk_out = current_security(); |
2514 | ssp->smk_packet[0] = '\0'; | 2497 | /* cssp->smk_packet is already set in smack_inet_csk_clone() */ |
2515 | |||
2516 | rc = smack_netlabel(sk, SMACK_CIPSO_SOCKET); | ||
2517 | if (rc != 0) | ||
2518 | printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n", | ||
2519 | __func__, -rc); | ||
2520 | } | 2498 | } |
2521 | 2499 | ||
2522 | /** | 2500 | /** |
@@ -2531,35 +2509,82 @@ static void smack_sock_graft(struct sock *sk, struct socket *parent) | |||
2531 | static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, | 2509 | static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, |
2532 | struct request_sock *req) | 2510 | struct request_sock *req) |
2533 | { | 2511 | { |
2534 | struct netlbl_lsm_secattr skb_secattr; | 2512 | u16 family = sk->sk_family; |
2535 | struct socket_smack *ssp = sk->sk_security; | 2513 | struct socket_smack *ssp = sk->sk_security; |
2514 | struct netlbl_lsm_secattr secattr; | ||
2515 | struct sockaddr_in addr; | ||
2516 | struct iphdr *hdr; | ||
2536 | char smack[SMK_LABELLEN]; | 2517 | char smack[SMK_LABELLEN]; |
2537 | int rc; | 2518 | int rc; |
2538 | 2519 | ||
2539 | if (skb == NULL) | 2520 | /* handle mapped IPv4 packets arriving via IPv6 sockets */ |
2540 | return -EACCES; | 2521 | if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) |
2522 | family = PF_INET; | ||
2541 | 2523 | ||
2542 | netlbl_secattr_init(&skb_secattr); | 2524 | netlbl_secattr_init(&secattr); |
2543 | rc = netlbl_skbuff_getattr(skb, sk->sk_family, &skb_secattr); | 2525 | rc = netlbl_skbuff_getattr(skb, family, &secattr); |
2544 | if (rc == 0) | 2526 | if (rc == 0) |
2545 | smack_from_secattr(&skb_secattr, smack); | 2527 | smack_from_secattr(&secattr, smack); |
2546 | else | 2528 | else |
2547 | strncpy(smack, smack_known_huh.smk_known, SMK_MAXLEN); | 2529 | strncpy(smack, smack_known_huh.smk_known, SMK_MAXLEN); |
2548 | netlbl_secattr_destroy(&skb_secattr); | 2530 | netlbl_secattr_destroy(&secattr); |
2531 | |||
2549 | /* | 2532 | /* |
2550 | * Receiving a packet requires that the other end | 2533 | * Receiving a packet requires that the other end be able to write |
2551 | * be able to write here. Read access is not required. | 2534 | * here. Read access is not required. |
2552 | * | ||
2553 | * If the request is successful save the peer's label | ||
2554 | * so that SO_PEERCRED can report it. | ||
2555 | */ | 2535 | */ |
2556 | rc = smk_access(smack, ssp->smk_in, MAY_WRITE); | 2536 | rc = smk_access(smack, ssp->smk_in, MAY_WRITE); |
2557 | if (rc == 0) | 2537 | if (rc != 0) |
2558 | strncpy(ssp->smk_packet, smack, SMK_MAXLEN); | 2538 | return rc; |
2539 | |||
2540 | /* | ||
2541 | * Save the peer's label in the request_sock so we can later setup | ||
2542 | * smk_packet in the child socket so that SO_PEERCRED can report it. | ||
2543 | */ | ||
2544 | req->peer_secid = smack_to_secid(smack); | ||
2545 | |||
2546 | /* | ||
2547 | * We need to decide if we want to label the incoming connection here | ||
2548 | * if we do we only need to label the request_sock and the stack will | ||
2549 | * propogate the wire-label to the sock when it is created. | ||
2550 | */ | ||
2551 | hdr = ip_hdr(skb); | ||
2552 | addr.sin_addr.s_addr = hdr->saddr; | ||
2553 | rcu_read_lock(); | ||
2554 | if (smack_host_label(&addr) == NULL) { | ||
2555 | rcu_read_unlock(); | ||
2556 | netlbl_secattr_init(&secattr); | ||
2557 | smack_to_secattr(smack, &secattr); | ||
2558 | rc = netlbl_req_setattr(req, &secattr); | ||
2559 | netlbl_secattr_destroy(&secattr); | ||
2560 | } else { | ||
2561 | rcu_read_unlock(); | ||
2562 | netlbl_req_delattr(req); | ||
2563 | } | ||
2559 | 2564 | ||
2560 | return rc; | 2565 | return rc; |
2561 | } | 2566 | } |
2562 | 2567 | ||
2568 | /** | ||
2569 | * smack_inet_csk_clone - Copy the connection information to the new socket | ||
2570 | * @sk: the new socket | ||
2571 | * @req: the connection's request_sock | ||
2572 | * | ||
2573 | * Transfer the connection's peer label to the newly created socket. | ||
2574 | */ | ||
2575 | static void smack_inet_csk_clone(struct sock *sk, | ||
2576 | const struct request_sock *req) | ||
2577 | { | ||
2578 | struct socket_smack *ssp = sk->sk_security; | ||
2579 | char *smack; | ||
2580 | |||
2581 | if (req->peer_secid != 0) { | ||
2582 | smack = smack_from_secid(req->peer_secid); | ||
2583 | strncpy(ssp->smk_packet, smack, SMK_MAXLEN); | ||
2584 | } else | ||
2585 | ssp->smk_packet[0] = '\0'; | ||
2586 | } | ||
2587 | |||
2563 | /* | 2588 | /* |
2564 | * Key management security hooks | 2589 | * Key management security hooks |
2565 | * | 2590 | * |
@@ -2911,6 +2936,7 @@ struct security_operations smack_ops = { | |||
2911 | .sk_free_security = smack_sk_free_security, | 2936 | .sk_free_security = smack_sk_free_security, |
2912 | .sock_graft = smack_sock_graft, | 2937 | .sock_graft = smack_sock_graft, |
2913 | .inet_conn_request = smack_inet_conn_request, | 2938 | .inet_conn_request = smack_inet_conn_request, |
2939 | .inet_csk_clone = smack_inet_csk_clone, | ||
2914 | 2940 | ||
2915 | /* key management security hooks */ | 2941 | /* key management security hooks */ |
2916 | #ifdef CONFIG_KEYS | 2942 | #ifdef CONFIG_KEYS |