Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/kern_jail.c
Show First 20 Lines • Show All 771 Lines • ▼ Show 20 Lines | prison_ip_set(struct prison *pr, const pr_family_t af, struct prison_ip *new) | ||||
old = *mem; | old = *mem; | ||||
ck_pr_store_ptr(mem, new); | ck_pr_store_ptr(mem, new); | ||||
prison_ip_free(old); | prison_ip_free(old); | ||||
} | } | ||||
/* | /* | ||||
* Restrict a prison's IP address list with its parent's, possibly replacing | * Restrict a prison's IP address list with its parent's, possibly replacing | ||||
* it. Return true if the replacement buffer was used (or should redo). | * it. Return true if succeed, otherwise should redo. | ||||
* kern_jail_set() helper. | * kern_jail_set() helper. | ||||
*/ | */ | ||||
static bool | static bool | ||||
prison_ip_restrict(struct prison *pr, const pr_family_t af, | prison_ip_restrict(struct prison *pr, const pr_family_t af, | ||||
struct prison_ip *new) | struct prison_ip **newp) | ||||
{ | { | ||||
const struct prison_ip *ppip = pr->pr_parent->pr_addrs[af]; | const struct prison_ip *ppip = pr->pr_parent->pr_addrs[af]; | ||||
const struct prison_ip *pip = pr->pr_addrs[af]; | const struct prison_ip *pip = pr->pr_addrs[af]; | ||||
int (*const cmp)(const void *, const void *) = pr_families[af].cmp; | int (*const cmp)(const void *, const void *) = pr_families[af].cmp; | ||||
const size_t size = pr_families[af].size; | const size_t size = pr_families[af].size; | ||||
struct prison_ip *new = newp != NULL ? *newp : NULL; | |||||
uint32_t ips; | uint32_t ips; | ||||
bool alloced, used; | |||||
mtx_assert(&pr->pr_mtx, MA_OWNED); | mtx_assert(&pr->pr_mtx, MA_OWNED); | ||||
/* | /* | ||||
* Due to epoch-synchronized access to the IP address lists we always | * Due to epoch-synchronized access to the IP address lists we always | ||||
* allocate a new list even if the old one has enough space. We could | * allocate a new list even if the old one has enough space. We could | ||||
* atomically update an IPv4 address inside a list, but that would | * atomically update an IPv4 address inside a list, but that would | ||||
* screw up sorting, and in case of IPv6 we can't even atomically write | * screw up sorting, and in case of IPv6 we can't even atomically write | ||||
* one. | * one. | ||||
*/ | */ | ||||
if (ppip == NULL) { | if (ppip == NULL) { | ||||
if (pip != NULL) | if (pip != NULL) | ||||
prison_ip_set(pr, af, NULL); | prison_ip_set(pr, af, NULL); | ||||
return (false); | return (true); | ||||
} | } | ||||
if (!(pr->pr_flags & pr_families[af].ip_flag)) { | if (!(pr->pr_flags & pr_families[af].ip_flag)) { | ||||
if (new == NULL) { | if (new == NULL) { | ||||
new = prison_ip_alloc(af, ppip->ips, M_NOWAIT); | new = prison_ip_alloc(af, ppip->ips, M_NOWAIT); | ||||
if (new == NULL) | if (new == NULL) | ||||
return (true); /* redo */ | return (false); /* Redo */ | ||||
used = false; | } | ||||
} else | |||||
used = true; | |||||
/* This has no user settings, so just copy the parent's list. */ | /* This has no user settings, so just copy the parent's list. */ | ||||
MPASS(new->ips == ppip->ips); | MPASS(new->ips == ppip->ips); | ||||
bcopy(ppip + 1, new + 1, ppip->ips * size); | bcopy(ppip + 1, new + 1, ppip->ips * size); | ||||
prison_ip_set(pr, af, new); | prison_ip_set(pr, af, new); | ||||
return (used); | if (newp != NULL) | ||||
*newp = NULL; /* Used */ | |||||
} else if (pip != NULL) { | } else if (pip != NULL) { | ||||
/* Remove addresses that aren't in the parent. */ | /* Remove addresses that aren't in the parent. */ | ||||
int i; | int i; | ||||
i = 0; /* index in pip */ | i = 0; /* index in pip */ | ||||
ips = 0; /* index in new */ | ips = 0; /* index in new */ | ||||
used = true; | |||||
if (new == NULL) { | if (new == NULL) { | ||||
new = prison_ip_alloc(af, pip->ips, M_NOWAIT); | new = prison_ip_alloc(af, pip->ips, M_NOWAIT); | ||||
if (new == NULL) | if (new == NULL) | ||||
return (true); /* redo */ | return (false); /* Redo */ | ||||
used = false; | |||||
alloced = true; | |||||
} else { | |||||
used = true; | |||||
alloced = false; | |||||
} | } | ||||
for (int pi = 0; pi < ppip->ips; pi++) | for (int pi = 0; pi < ppip->ips; pi++) | ||||
if (cmp(PR_IP(pip, 0), PR_IP(ppip, pi)) == 0) { | if (cmp(PR_IP(pip, 0), PR_IP(ppip, pi)) == 0) { | ||||
/* Found our primary address in parent. */ | /* Found our primary address in parent. */ | ||||
bcopy(PR_IP(pip, i), PR_IPD(new, ips), size); | bcopy(PR_IP(pip, i), PR_IPD(new, ips), size); | ||||
i++; | i++; | ||||
ips++; | ips++; | ||||
Show All 21 Lines | for (int pi = 1; i < pip->ips; ) { | ||||
ips++; | ips++; | ||||
break; | break; | ||||
case 1: | case 1: | ||||
pi++; | pi++; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if (ips == 0) { | if (ips == 0) { | ||||
if (alloced) | if (newp == NULL || *newp == NULL) | ||||
prison_ip_free(new); | prison_ip_free(new); | ||||
new = NULL; | new = NULL; | ||||
used = false; | |||||
} else { | } else { | ||||
/* Shrink to real size */ | /* Shrink to real size */ | ||||
KASSERT((new->ips >= ips), | KASSERT((new->ips >= ips), | ||||
("Out-of-bounds write to prison_ip %p", new)); | ("Out-of-bounds write to prison_ip %p", new)); | ||||
new->ips = ips; | new->ips = ips; | ||||
} | } | ||||
prison_ip_set(pr, af, new); | prison_ip_set(pr, af, new); | ||||
return (used); | if (newp != NULL) | ||||
*newp = NULL; /* Used */ | |||||
} | } | ||||
return (false); | return (true); | ||||
} | } | ||||
/* | /* | ||||
* Fast-path check if an address belongs to a prison. | * Fast-path check if an address belongs to a prison. | ||||
*/ | */ | ||||
int | int | ||||
prison_ip_check(const struct prison *pr, const pr_family_t af, | prison_ip_check(const struct prison *pr, const pr_family_t af, | ||||
const void *addr) | const void *addr) | ||||
▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | #endif | ||||
unsigned long hid; | unsigned long hid; | ||||
size_t namelen, onamelen, pnamelen; | size_t namelen, onamelen, pnamelen; | ||||
int born, created, cuflags, descend, drflags, enforce; | int born, created, cuflags, descend, drflags, enforce; | ||||
int error, errmsg_len, errmsg_pos; | int error, errmsg_len, errmsg_pos; | ||||
int gotchildmax, gotenforce, gothid, gotrsnum, gotslevel; | int gotchildmax, gotenforce, gothid, gotrsnum, gotslevel; | ||||
int jid, jsys, len, level; | int jid, jsys, len, level; | ||||
int childmax, osreldt, rsnum, slevel; | int childmax, osreldt, rsnum, slevel; | ||||
#ifdef INET | #ifdef INET | ||||
int ip4s, redo_ip4; | int ip4s; | ||||
bool redo_ip4; | |||||
#endif | #endif | ||||
#ifdef INET6 | #ifdef INET6 | ||||
int ip6s, redo_ip6; | int ip6s; | ||||
bool redo_ip6; | |||||
#endif | #endif | ||||
uint64_t pr_allow, ch_allow, pr_flags, ch_flags; | uint64_t pr_allow, ch_allow, pr_flags, ch_flags; | ||||
uint64_t pr_allow_diff; | uint64_t pr_allow_diff; | ||||
unsigned tallow; | unsigned tallow; | ||||
char numbuf[12]; | char numbuf[12]; | ||||
error = priv_check(td, PRIV_JAIL_SET); | error = priv_check(td, PRIV_JAIL_SET); | ||||
if (!error && (flags & JAIL_ATTACH)) | if (!error && (flags & JAIL_ATTACH)) | ||||
▲ Show 20 Lines • Show All 860 Lines • ▼ Show 20 Lines | if (!opt->seen && strcmp(opt->name, "errmsg")) { | ||||
error = EINVAL; | error = EINVAL; | ||||
vfs_opterror(opts, "unknown parameter: %s", opt->name); | vfs_opterror(opts, "unknown parameter: %s", opt->name); | ||||
goto done_deref; | goto done_deref; | ||||
} | } | ||||
} | } | ||||
/* Set the parameters of the prison. */ | /* Set the parameters of the prison. */ | ||||
#ifdef INET | #ifdef INET | ||||
redo_ip4 = 0; | redo_ip4 = false; | ||||
if (pr_flags & PR_IP4_USER) { | if (pr_flags & PR_IP4_USER) { | ||||
pr->pr_flags |= PR_IP4; | pr->pr_flags |= PR_IP4; | ||||
prison_ip_set(pr, PR_INET, ip4); | prison_ip_set(pr, PR_INET, ip4); | ||||
ip4 = NULL; | ip4 = NULL; | ||||
FOREACH_PRISON_DESCENDANT_LOCKED(pr, tpr, descend) { | FOREACH_PRISON_DESCENDANT_LOCKED(pr, tpr, descend) { | ||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
if (tpr->pr_flags & PR_VNET) { | if (tpr->pr_flags & PR_VNET) { | ||||
descend = 0; | descend = 0; | ||||
continue; | continue; | ||||
} | } | ||||
#endif | #endif | ||||
if (prison_ip_restrict(tpr, PR_INET, NULL)) { | if (!prison_ip_restrict(tpr, PR_INET, NULL)) { | ||||
redo_ip4 = 1; | redo_ip4 = true; | ||||
descend = 0; | descend = 0; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
#endif | #endif | ||||
#ifdef INET6 | #ifdef INET6 | ||||
redo_ip6 = 0; | redo_ip6 = false; | ||||
if (pr_flags & PR_IP6_USER) { | if (pr_flags & PR_IP6_USER) { | ||||
pr->pr_flags |= PR_IP6; | pr->pr_flags |= PR_IP6; | ||||
prison_ip_set(pr, PR_INET6, ip6); | prison_ip_set(pr, PR_INET6, ip6); | ||||
ip6 = NULL; | ip6 = NULL; | ||||
FOREACH_PRISON_DESCENDANT_LOCKED(pr, tpr, descend) { | FOREACH_PRISON_DESCENDANT_LOCKED(pr, tpr, descend) { | ||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
if (tpr->pr_flags & PR_VNET) { | if (tpr->pr_flags & PR_VNET) { | ||||
descend = 0; | descend = 0; | ||||
continue; | continue; | ||||
} | } | ||||
#endif | #endif | ||||
if (prison_ip_restrict(tpr, PR_INET6, NULL)) { | if (!prison_ip_restrict(tpr, PR_INET6, NULL)) { | ||||
redo_ip6 = 1; | redo_ip6 = true; | ||||
descend = 0; | descend = 0; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
#endif | #endif | ||||
if (gotslevel) { | if (gotslevel) { | ||||
pr->pr_securelevel = slevel; | pr->pr_securelevel = slevel; | ||||
/* Set all child jails to be at least this level. */ | /* Set all child jails to be at least this level. */ | ||||
▲ Show 20 Lines • Show All 127 Lines • ▼ Show 20 Lines | |||||
#endif | #endif | ||||
/* Locks may have prevented a complete restriction of child IP | /* Locks may have prevented a complete restriction of child IP | ||||
* addresses. If so, allocate some more memory and try again. | * addresses. If so, allocate some more memory and try again. | ||||
*/ | */ | ||||
#ifdef INET | #ifdef INET | ||||
while (redo_ip4) { | while (redo_ip4) { | ||||
ip4s = pr->pr_addrs[PR_INET]->ips; | ip4s = pr->pr_addrs[PR_INET]->ips; | ||||
MPASS(ip4 == NULL); | |||||
ip4 = prison_ip_alloc(PR_INET, ip4s, M_WAITOK); | ip4 = prison_ip_alloc(PR_INET, ip4s, M_WAITOK); | ||||
mtx_lock(&pr->pr_mtx); | mtx_lock(&pr->pr_mtx); | ||||
redo_ip4 = 0; | redo_ip4 = false; | ||||
FOREACH_PRISON_DESCENDANT_LOCKED(pr, tpr, descend) { | FOREACH_PRISON_DESCENDANT_LOCKED(pr, tpr, descend) { | ||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
if (tpr->pr_flags & PR_VNET) { | if (tpr->pr_flags & PR_VNET) { | ||||
descend = 0; | descend = 0; | ||||
continue; | continue; | ||||
} | } | ||||
#endif | #endif | ||||
if (prison_ip_restrict(tpr, PR_INET, ip4)) { | redo_ip4 = !prison_ip_restrict(tpr, PR_INET, &ip4); | ||||
if (ip4 != NULL) | |||||
ip4 = NULL; | |||||
else | |||||
redo_ip4 = 1; | |||||
} | } | ||||
glebius: Type of redo_ip4 can be changed to bool and this expression reduced to:
redo_ip4 = ! | |||||
Done Inline ActionsGood catch! zlei: Good catch! | |||||
} | |||||
mtx_unlock(&pr->pr_mtx); | mtx_unlock(&pr->pr_mtx); | ||||
} | } | ||||
#endif | #endif | ||||
#ifdef INET6 | #ifdef INET6 | ||||
while (redo_ip6) { | while (redo_ip6) { | ||||
ip6s = pr->pr_addrs[PR_INET6]->ips; | ip6s = pr->pr_addrs[PR_INET6]->ips; | ||||
MPASS(ip6 == NULL); | |||||
ip6 = prison_ip_alloc(PR_INET6, ip6s, M_WAITOK); | ip6 = prison_ip_alloc(PR_INET6, ip6s, M_WAITOK); | ||||
mtx_lock(&pr->pr_mtx); | mtx_lock(&pr->pr_mtx); | ||||
redo_ip6 = 0; | redo_ip6 = false; | ||||
FOREACH_PRISON_DESCENDANT_LOCKED(pr, tpr, descend) { | FOREACH_PRISON_DESCENDANT_LOCKED(pr, tpr, descend) { | ||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
if (tpr->pr_flags & PR_VNET) { | if (tpr->pr_flags & PR_VNET) { | ||||
descend = 0; | descend = 0; | ||||
continue; | continue; | ||||
} | } | ||||
#endif | #endif | ||||
if (prison_ip_restrict(tpr, PR_INET6, ip6)) { | redo_ip6 = !prison_ip_restrict(tpr, PR_INET6, &ip6); | ||||
if (ip6 != NULL) | |||||
ip6 = NULL; | |||||
else | |||||
redo_ip6 = 1; | |||||
} | |||||
} | } | ||||
mtx_unlock(&pr->pr_mtx); | mtx_unlock(&pr->pr_mtx); | ||||
} | } | ||||
#endif | #endif | ||||
/* Let the modules do their work. */ | /* Let the modules do their work. */ | ||||
if (born) { | if (born) { | ||||
error = osd_jail_call(pr, PR_METHOD_CREATE, opts); | error = osd_jail_call(pr, PR_METHOD_CREATE, opts); | ||||
▲ Show 20 Lines • Show All 2,912 Lines • Show Last 20 Lines |
Type of redo_ip4 can be changed to bool and this expression reduced to:
redo_ip4 = !prison_ip_restrict(tpr, PR_INET, &ip4);
Same applies to redo_ip6.