diff options
-rw-r--r-- | arch/x86/mm/fault.c | 2 | ||||
-rw-r--r-- | fs/ioprio.c | 8 | ||||
-rw-r--r-- | include/linux/cred.h | 16 | ||||
-rw-r--r-- | include/linux/user_namespace.h | 8 | ||||
-rw-r--r-- | kernel/cred.c | 36 | ||||
-rw-r--r-- | kernel/signal.c | 14 | ||||
-rw-r--r-- | kernel/sys.c | 26 | ||||
-rw-r--r-- | kernel/user_namespace.c | 4 | ||||
-rw-r--r-- | mm/oom_kill.c | 4 | ||||
-rw-r--r-- | security/commoncap.c | 3 |
10 files changed, 59 insertions, 62 deletions
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 3ecfd1aaf214..76dcd9d8e0bc 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c | |||
@@ -582,7 +582,7 @@ show_fault_oops(struct pt_regs *regs, unsigned long error_code, | |||
582 | pte_t *pte = lookup_address(address, &level); | 582 | pte_t *pte = lookup_address(address, &level); |
583 | 583 | ||
584 | if (pte && pte_present(*pte) && !pte_exec(*pte)) | 584 | if (pte && pte_present(*pte) && !pte_exec(*pte)) |
585 | printk(nx_warning, current_uid()); | 585 | printk(nx_warning, from_kuid(&init_user_ns, current_uid())); |
586 | } | 586 | } |
587 | 587 | ||
588 | printk(KERN_ALERT "BUG: unable to handle kernel "); | 588 | printk(KERN_ALERT "BUG: unable to handle kernel "); |
diff --git a/fs/ioprio.c b/fs/ioprio.c index 8e35e964d9ed..2072e41785d2 100644 --- a/fs/ioprio.c +++ b/fs/ioprio.c | |||
@@ -123,9 +123,7 @@ SYSCALL_DEFINE3(ioprio_set, int, which, int, who, int, ioprio) | |||
123 | break; | 123 | break; |
124 | 124 | ||
125 | do_each_thread(g, p) { | 125 | do_each_thread(g, p) { |
126 | const struct cred *tcred = __task_cred(p); | 126 | if (!uid_eq(task_uid(p), uid)) |
127 | kuid_t tcred_uid = make_kuid(tcred->user_ns, tcred->uid); | ||
128 | if (!uid_eq(tcred_uid, uid)) | ||
129 | continue; | 127 | continue; |
130 | ret = set_task_ioprio(p, ioprio); | 128 | ret = set_task_ioprio(p, ioprio); |
131 | if (ret) | 129 | if (ret) |
@@ -220,9 +218,7 @@ SYSCALL_DEFINE2(ioprio_get, int, which, int, who) | |||
220 | break; | 218 | break; |
221 | 219 | ||
222 | do_each_thread(g, p) { | 220 | do_each_thread(g, p) { |
223 | const struct cred *tcred = __task_cred(p); | 221 | if (!uid_eq(task_uid(p), user->uid)) |
224 | kuid_t tcred_uid = make_kuid(tcred->user_ns, tcred->uid); | ||
225 | if (!uid_eq(tcred_uid, user->uid)) | ||
226 | continue; | 222 | continue; |
227 | tmpio = get_task_ioprio(p); | 223 | tmpio = get_task_ioprio(p); |
228 | if (tmpio < 0) | 224 | if (tmpio < 0) |
diff --git a/include/linux/cred.h b/include/linux/cred.h index 0ab3cda4a774..fac0579258fc 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h | |||
@@ -123,14 +123,14 @@ struct cred { | |||
123 | #define CRED_MAGIC 0x43736564 | 123 | #define CRED_MAGIC 0x43736564 |
124 | #define CRED_MAGIC_DEAD 0x44656144 | 124 | #define CRED_MAGIC_DEAD 0x44656144 |
125 | #endif | 125 | #endif |
126 | uid_t uid; /* real UID of the task */ | 126 | kuid_t uid; /* real UID of the task */ |
127 | gid_t gid; /* real GID of the task */ | 127 | kgid_t gid; /* real GID of the task */ |
128 | uid_t suid; /* saved UID of the task */ | 128 | kuid_t suid; /* saved UID of the task */ |
129 | gid_t sgid; /* saved GID of the task */ | 129 | kgid_t sgid; /* saved GID of the task */ |
130 | uid_t euid; /* effective UID of the task */ | 130 | kuid_t euid; /* effective UID of the task */ |
131 | gid_t egid; /* effective GID of the task */ | 131 | kgid_t egid; /* effective GID of the task */ |
132 | uid_t fsuid; /* UID for VFS ops */ | 132 | kuid_t fsuid; /* UID for VFS ops */ |
133 | gid_t fsgid; /* GID for VFS ops */ | 133 | kgid_t fsgid; /* GID for VFS ops */ |
134 | unsigned securebits; /* SUID-less security management */ | 134 | unsigned securebits; /* SUID-less security management */ |
135 | kernel_cap_t cap_inheritable; /* caps our children can inherit */ | 135 | kernel_cap_t cap_inheritable; /* caps our children can inherit */ |
136 | kernel_cap_t cap_permitted; /* caps we're permitted */ | 136 | kernel_cap_t cap_permitted; /* caps we're permitted */ |
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index 4c9846d90741..a2c61457cba1 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h | |||
@@ -70,15 +70,15 @@ static inline void put_user_ns(struct user_namespace *ns) | |||
70 | #endif | 70 | #endif |
71 | 71 | ||
72 | static inline uid_t user_ns_map_uid(struct user_namespace *to, | 72 | static inline uid_t user_ns_map_uid(struct user_namespace *to, |
73 | const struct cred *cred, uid_t uid) | 73 | const struct cred *cred, kuid_t uid) |
74 | { | 74 | { |
75 | return from_kuid_munged(to, make_kuid(cred->user_ns, uid)); | 75 | return from_kuid_munged(to, uid); |
76 | } | 76 | } |
77 | 77 | ||
78 | static inline gid_t user_ns_map_gid(struct user_namespace *to, | 78 | static inline gid_t user_ns_map_gid(struct user_namespace *to, |
79 | const struct cred *cred, gid_t gid) | 79 | const struct cred *cred, kgid_t gid) |
80 | { | 80 | { |
81 | return from_kgid_munged(to, make_kgid(cred->user_ns, gid)); | 81 | return from_kgid_munged(to, gid); |
82 | } | 82 | } |
83 | 83 | ||
84 | #endif /* _LINUX_USER_H */ | 84 | #endif /* _LINUX_USER_H */ |
diff --git a/kernel/cred.c b/kernel/cred.c index 7a0d80669886..eddc5e2e9587 100644 --- a/kernel/cred.c +++ b/kernel/cred.c | |||
@@ -49,6 +49,14 @@ struct cred init_cred = { | |||
49 | .subscribers = ATOMIC_INIT(2), | 49 | .subscribers = ATOMIC_INIT(2), |
50 | .magic = CRED_MAGIC, | 50 | .magic = CRED_MAGIC, |
51 | #endif | 51 | #endif |
52 | .uid = GLOBAL_ROOT_UID, | ||
53 | .gid = GLOBAL_ROOT_GID, | ||
54 | .suid = GLOBAL_ROOT_UID, | ||
55 | .sgid = GLOBAL_ROOT_GID, | ||
56 | .euid = GLOBAL_ROOT_UID, | ||
57 | .egid = GLOBAL_ROOT_GID, | ||
58 | .fsuid = GLOBAL_ROOT_UID, | ||
59 | .fsgid = GLOBAL_ROOT_GID, | ||
52 | .securebits = SECUREBITS_DEFAULT, | 60 | .securebits = SECUREBITS_DEFAULT, |
53 | .cap_inheritable = CAP_EMPTY_SET, | 61 | .cap_inheritable = CAP_EMPTY_SET, |
54 | .cap_permitted = CAP_FULL_SET, | 62 | .cap_permitted = CAP_FULL_SET, |
@@ -488,10 +496,10 @@ int commit_creds(struct cred *new) | |||
488 | get_cred(new); /* we will require a ref for the subj creds too */ | 496 | get_cred(new); /* we will require a ref for the subj creds too */ |
489 | 497 | ||
490 | /* dumpability changes */ | 498 | /* dumpability changes */ |
491 | if (old->euid != new->euid || | 499 | if (!uid_eq(old->euid, new->euid) || |
492 | old->egid != new->egid || | 500 | !gid_eq(old->egid, new->egid) || |
493 | old->fsuid != new->fsuid || | 501 | !uid_eq(old->fsuid, new->fsuid) || |
494 | old->fsgid != new->fsgid || | 502 | !gid_eq(old->fsgid, new->fsgid) || |
495 | !cap_issubset(new->cap_permitted, old->cap_permitted)) { | 503 | !cap_issubset(new->cap_permitted, old->cap_permitted)) { |
496 | if (task->mm) | 504 | if (task->mm) |
497 | set_dumpable(task->mm, suid_dumpable); | 505 | set_dumpable(task->mm, suid_dumpable); |
@@ -500,9 +508,9 @@ int commit_creds(struct cred *new) | |||
500 | } | 508 | } |
501 | 509 | ||
502 | /* alter the thread keyring */ | 510 | /* alter the thread keyring */ |
503 | if (new->fsuid != old->fsuid) | 511 | if (!uid_eq(new->fsuid, old->fsuid)) |
504 | key_fsuid_changed(task); | 512 | key_fsuid_changed(task); |
505 | if (new->fsgid != old->fsgid) | 513 | if (!gid_eq(new->fsgid, old->fsgid)) |
506 | key_fsgid_changed(task); | 514 | key_fsgid_changed(task); |
507 | 515 | ||
508 | /* do it | 516 | /* do it |
@@ -519,16 +527,16 @@ int commit_creds(struct cred *new) | |||
519 | alter_cred_subscribers(old, -2); | 527 | alter_cred_subscribers(old, -2); |
520 | 528 | ||
521 | /* send notifications */ | 529 | /* send notifications */ |
522 | if (new->uid != old->uid || | 530 | if (!uid_eq(new->uid, old->uid) || |
523 | new->euid != old->euid || | 531 | !uid_eq(new->euid, old->euid) || |
524 | new->suid != old->suid || | 532 | !uid_eq(new->suid, old->suid) || |
525 | new->fsuid != old->fsuid) | 533 | !uid_eq(new->fsuid, old->fsuid)) |
526 | proc_id_connector(task, PROC_EVENT_UID); | 534 | proc_id_connector(task, PROC_EVENT_UID); |
527 | 535 | ||
528 | if (new->gid != old->gid || | 536 | if (!gid_eq(new->gid, old->gid) || |
529 | new->egid != old->egid || | 537 | !gid_eq(new->egid, old->egid) || |
530 | new->sgid != old->sgid || | 538 | !gid_eq(new->sgid, old->sgid) || |
531 | new->fsgid != old->fsgid) | 539 | !gid_eq(new->fsgid, old->fsgid)) |
532 | proc_id_connector(task, PROC_EVENT_GID); | 540 | proc_id_connector(task, PROC_EVENT_GID); |
533 | 541 | ||
534 | /* release the old obj and subj refs both */ | 542 | /* release the old obj and subj refs both */ |
diff --git a/kernel/signal.c b/kernel/signal.c index e2c5d84f2dac..2734dc965f69 100644 --- a/kernel/signal.c +++ b/kernel/signal.c | |||
@@ -1038,8 +1038,10 @@ static inline void userns_fixup_signal_uid(struct siginfo *info, struct task_str | |||
1038 | if (SI_FROMKERNEL(info)) | 1038 | if (SI_FROMKERNEL(info)) |
1039 | return; | 1039 | return; |
1040 | 1040 | ||
1041 | info->si_uid = user_ns_map_uid(task_cred_xxx(t, user_ns), | 1041 | rcu_read_lock(); |
1042 | current_cred(), info->si_uid); | 1042 | info->si_uid = from_kuid_munged(task_cred_xxx(t, user_ns), |
1043 | make_kuid(current_user_ns(), info->si_uid)); | ||
1044 | rcu_read_unlock(); | ||
1043 | } | 1045 | } |
1044 | #else | 1046 | #else |
1045 | static inline void userns_fixup_signal_uid(struct siginfo *info, struct task_struct *t) | 1047 | static inline void userns_fixup_signal_uid(struct siginfo *info, struct task_struct *t) |
@@ -1106,7 +1108,7 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t, | |||
1106 | q->info.si_code = SI_USER; | 1108 | q->info.si_code = SI_USER; |
1107 | q->info.si_pid = task_tgid_nr_ns(current, | 1109 | q->info.si_pid = task_tgid_nr_ns(current, |
1108 | task_active_pid_ns(t)); | 1110 | task_active_pid_ns(t)); |
1109 | q->info.si_uid = current_uid(); | 1111 | q->info.si_uid = from_kuid_munged(current_user_ns(), current_uid()); |
1110 | break; | 1112 | break; |
1111 | case (unsigned long) SEND_SIG_PRIV: | 1113 | case (unsigned long) SEND_SIG_PRIV: |
1112 | q->info.si_signo = sig; | 1114 | q->info.si_signo = sig; |
@@ -1973,7 +1975,7 @@ static void ptrace_do_notify(int signr, int exit_code, int why) | |||
1973 | info.si_signo = signr; | 1975 | info.si_signo = signr; |
1974 | info.si_code = exit_code; | 1976 | info.si_code = exit_code; |
1975 | info.si_pid = task_pid_vnr(current); | 1977 | info.si_pid = task_pid_vnr(current); |
1976 | info.si_uid = current_uid(); | 1978 | info.si_uid = from_kuid_munged(current_user_ns(), current_uid()); |
1977 | 1979 | ||
1978 | /* Let the debugger run. */ | 1980 | /* Let the debugger run. */ |
1979 | ptrace_stop(exit_code, why, 1, &info); | 1981 | ptrace_stop(exit_code, why, 1, &info); |
@@ -2828,7 +2830,7 @@ SYSCALL_DEFINE2(kill, pid_t, pid, int, sig) | |||
2828 | info.si_errno = 0; | 2830 | info.si_errno = 0; |
2829 | info.si_code = SI_USER; | 2831 | info.si_code = SI_USER; |
2830 | info.si_pid = task_tgid_vnr(current); | 2832 | info.si_pid = task_tgid_vnr(current); |
2831 | info.si_uid = current_uid(); | 2833 | info.si_uid = from_kuid_munged(current_user_ns(), current_uid()); |
2832 | 2834 | ||
2833 | return kill_something_info(sig, &info, pid); | 2835 | return kill_something_info(sig, &info, pid); |
2834 | } | 2836 | } |
@@ -2871,7 +2873,7 @@ static int do_tkill(pid_t tgid, pid_t pid, int sig) | |||
2871 | info.si_errno = 0; | 2873 | info.si_errno = 0; |
2872 | info.si_code = SI_TKILL; | 2874 | info.si_code = SI_TKILL; |
2873 | info.si_pid = task_tgid_vnr(current); | 2875 | info.si_pid = task_tgid_vnr(current); |
2874 | info.si_uid = current_uid(); | 2876 | info.si_uid = from_kuid_munged(current_user_ns(), current_uid()); |
2875 | 2877 | ||
2876 | return do_send_specific(tgid, pid, sig, &info); | 2878 | return do_send_specific(tgid, pid, sig, &info); |
2877 | } | 2879 | } |
diff --git a/kernel/sys.c b/kernel/sys.c index f0c43b4b6657..39962818c008 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
@@ -175,7 +175,6 @@ SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval) | |||
175 | const struct cred *cred = current_cred(); | 175 | const struct cred *cred = current_cred(); |
176 | int error = -EINVAL; | 176 | int error = -EINVAL; |
177 | struct pid *pgrp; | 177 | struct pid *pgrp; |
178 | kuid_t cred_uid; | ||
179 | kuid_t uid; | 178 | kuid_t uid; |
180 | 179 | ||
181 | if (which > PRIO_USER || which < PRIO_PROCESS) | 180 | if (which > PRIO_USER || which < PRIO_PROCESS) |
@@ -209,22 +208,19 @@ SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval) | |||
209 | } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); | 208 | } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); |
210 | break; | 209 | break; |
211 | case PRIO_USER: | 210 | case PRIO_USER: |
212 | cred_uid = make_kuid(cred->user_ns, cred->uid); | ||
213 | uid = make_kuid(cred->user_ns, who); | 211 | uid = make_kuid(cred->user_ns, who); |
214 | user = cred->user; | 212 | user = cred->user; |
215 | if (!who) | 213 | if (!who) |
216 | uid = cred_uid; | 214 | uid = cred->uid; |
217 | else if (!uid_eq(uid, cred_uid) && | 215 | else if (!uid_eq(uid, cred->uid) && |
218 | !(user = find_user(uid))) | 216 | !(user = find_user(uid))) |
219 | goto out_unlock; /* No processes for this user */ | 217 | goto out_unlock; /* No processes for this user */ |
220 | 218 | ||
221 | do_each_thread(g, p) { | 219 | do_each_thread(g, p) { |
222 | const struct cred *tcred = __task_cred(p); | 220 | if (uid_eq(task_uid(p), uid)) |
223 | kuid_t tcred_uid = make_kuid(tcred->user_ns, tcred->uid); | ||
224 | if (uid_eq(tcred_uid, uid)) | ||
225 | error = set_one_prio(p, niceval, error); | 221 | error = set_one_prio(p, niceval, error); |
226 | } while_each_thread(g, p); | 222 | } while_each_thread(g, p); |
227 | if (!uid_eq(uid, cred_uid)) | 223 | if (!uid_eq(uid, cred->uid)) |
228 | free_uid(user); /* For find_user() */ | 224 | free_uid(user); /* For find_user() */ |
229 | break; | 225 | break; |
230 | } | 226 | } |
@@ -248,7 +244,6 @@ SYSCALL_DEFINE2(getpriority, int, which, int, who) | |||
248 | const struct cred *cred = current_cred(); | 244 | const struct cred *cred = current_cred(); |
249 | long niceval, retval = -ESRCH; | 245 | long niceval, retval = -ESRCH; |
250 | struct pid *pgrp; | 246 | struct pid *pgrp; |
251 | kuid_t cred_uid; | ||
252 | kuid_t uid; | 247 | kuid_t uid; |
253 | 248 | ||
254 | if (which > PRIO_USER || which < PRIO_PROCESS) | 249 | if (which > PRIO_USER || which < PRIO_PROCESS) |
@@ -280,25 +275,22 @@ SYSCALL_DEFINE2(getpriority, int, which, int, who) | |||
280 | } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); | 275 | } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); |
281 | break; | 276 | break; |
282 | case PRIO_USER: | 277 | case PRIO_USER: |
283 | cred_uid = make_kuid(cred->user_ns, cred->uid); | ||
284 | uid = make_kuid(cred->user_ns, who); | 278 | uid = make_kuid(cred->user_ns, who); |
285 | user = cred->user; | 279 | user = cred->user; |
286 | if (!who) | 280 | if (!who) |
287 | uid = cred_uid; | 281 | uid = cred->uid; |
288 | else if (!uid_eq(uid, cred_uid) && | 282 | else if (!uid_eq(uid, cred->uid) && |
289 | !(user = find_user(uid))) | 283 | !(user = find_user(uid))) |
290 | goto out_unlock; /* No processes for this user */ | 284 | goto out_unlock; /* No processes for this user */ |
291 | 285 | ||
292 | do_each_thread(g, p) { | 286 | do_each_thread(g, p) { |
293 | const struct cred *tcred = __task_cred(p); | 287 | if (uid_eq(task_uid(p), uid)) { |
294 | kuid_t tcred_uid = make_kuid(tcred->user_ns, tcred->uid); | ||
295 | if (uid_eq(tcred_uid, uid)) { | ||
296 | niceval = 20 - task_nice(p); | 288 | niceval = 20 - task_nice(p); |
297 | if (niceval > retval) | 289 | if (niceval > retval) |
298 | retval = niceval; | 290 | retval = niceval; |
299 | } | 291 | } |
300 | } while_each_thread(g, p); | 292 | } while_each_thread(g, p); |
301 | if (!uid_eq(uid, cred_uid)) | 293 | if (!uid_eq(uid, cred->uid)) |
302 | free_uid(user); /* for find_user() */ | 294 | free_uid(user); /* for find_user() */ |
303 | break; | 295 | break; |
304 | } | 296 | } |
@@ -641,7 +633,7 @@ static int set_user(struct cred *new) | |||
641 | { | 633 | { |
642 | struct user_struct *new_user; | 634 | struct user_struct *new_user; |
643 | 635 | ||
644 | new_user = alloc_uid(make_kuid(new->user_ns, new->uid)); | 636 | new_user = alloc_uid(new->uid); |
645 | if (!new_user) | 637 | if (!new_user) |
646 | return -EAGAIN; | 638 | return -EAGAIN; |
647 | 639 | ||
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 7eff867bfac5..86602316422d 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c | |||
@@ -36,8 +36,8 @@ static bool new_idmap_permitted(struct user_namespace *ns, int cap_setid, | |||
36 | int create_user_ns(struct cred *new) | 36 | int create_user_ns(struct cred *new) |
37 | { | 37 | { |
38 | struct user_namespace *ns, *parent_ns = new->user_ns; | 38 | struct user_namespace *ns, *parent_ns = new->user_ns; |
39 | kuid_t owner = make_kuid(new->user_ns, new->euid); | 39 | kuid_t owner = new->euid; |
40 | kgid_t group = make_kgid(new->user_ns, new->egid); | 40 | kgid_t group = new->egid; |
41 | 41 | ||
42 | /* The creator needs a mapping in the parent user namespace | 42 | /* The creator needs a mapping in the parent user namespace |
43 | * or else we won't be able to reasonably tell userspace who | 43 | * or else we won't be able to reasonably tell userspace who |
diff --git a/mm/oom_kill.c b/mm/oom_kill.c index 46bf2ed5594c..9f09a1fde9f9 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c | |||
@@ -410,8 +410,8 @@ static void dump_tasks(const struct mem_cgroup *memcg, const nodemask_t *nodemas | |||
410 | } | 410 | } |
411 | 411 | ||
412 | pr_info("[%5d] %5d %5d %8lu %8lu %3u %3d %5d %s\n", | 412 | pr_info("[%5d] %5d %5d %8lu %8lu %3u %3d %5d %s\n", |
413 | task->pid, task_uid(task), task->tgid, | 413 | task->pid, from_kuid(&init_user_ns, task_uid(task)), |
414 | task->mm->total_vm, get_mm_rss(task->mm), | 414 | task->tgid, task->mm->total_vm, get_mm_rss(task->mm), |
415 | task_cpu(task), task->signal->oom_adj, | 415 | task_cpu(task), task->signal->oom_adj, |
416 | task->signal->oom_score_adj, task->comm); | 416 | task->signal->oom_score_adj, task->comm); |
417 | task_unlock(task); | 417 | task_unlock(task); |
diff --git a/security/commoncap.c b/security/commoncap.c index f2399d8afbe0..dbd465a59286 100644 --- a/security/commoncap.c +++ b/security/commoncap.c | |||
@@ -77,8 +77,7 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, | |||
77 | { | 77 | { |
78 | for (;;) { | 78 | for (;;) { |
79 | /* The owner of the user namespace has all caps. */ | 79 | /* The owner of the user namespace has all caps. */ |
80 | if (targ_ns != &init_user_ns && uid_eq(targ_ns->owner, | 80 | if (targ_ns != &init_user_ns && uid_eq(targ_ns->owner, cred->euid)) |
81 | make_kuid(cred->user_ns, cred->euid))) | ||
82 | return 0; | 81 | return 0; |
83 | 82 | ||
84 | /* Do we have the necessary capabilities? */ | 83 | /* Do we have the necessary capabilities? */ |