diff --git a/conts/posix/mm0/mm/capability.c b/conts/posix/mm0/mm/capability.c index 1e71661..2471408 100644 --- a/conts/posix/mm0/mm/capability.c +++ b/conts/posix/mm0/mm/capability.c @@ -347,73 +347,86 @@ int cap_find_replicate_reduce_grant(struct capability *cap) struct capability new_cap; int err; - /* - * Check for type - */ + /* Merely match type, kernel does actual check on suitability */ list_foreach_struct(possessed, &capability_list.caps, list) { - if (cap_type(possessed) == cap_type(cap)) - goto found; + /* Different type, pass */ + if (cap_type(possessed) != cap_type(cap)) + continue; + + /* Copy possessed one to new one's buffer */ + memcpy(&new_cap, possessed, sizeof(*possessed)); + + /* Replicate capability, giving original as reference */ + if ((err = l4_capability_control(CAP_CONTROL_REPLICATE, + 0, 0, 0, &new_cap)) < 0) { + printf("l4_capability_control() replication of " + "capability failed.\n Could not complete " + "CAP_CONTROL_REPLICATE request on cap (%d), " + "err = %d.\n", new_cap.capid, err); + return err; + } + + /* + * The returned capability is a replica. + * + * We don't add the newly created one to our own internal + * list because we will grant it shortly and lose its + * possession + * + * Now deduce it such that it looks like the one requested. + * Note, we assume the request had been validated before. + * Also note, the owner shall be still us. + */ + new_cap.resid = cap->resid; + new_cap.type = cap->type; + new_cap.access = cap->access; + new_cap.start = cap->start; + new_cap.end = cap->end; + new_cap.size = cap->size; + new_cap.used = cap->used; + + /* + * Make sure it is transferable, + * since we will need to grant it soon + */ + new_cap.access |= CAP_TRANSFERABLE; + + + if ((err = l4_capability_control(CAP_CONTROL_DEDUCE, + 0, 0, 0, &new_cap)) < 0) { + /* Couldn't deduce this one, destroy the replica */ + if ((err = + l4_capability_control(CAP_CONTROL_DESTROY, + 0, 0, 0, &new_cap)) < 0) { + printf("l4_capability_control() replication of " + "capability failed.\n Could not " + "complete CAP_CONTROL_DESTROY request " + " on cap (%d), err = %d.\n", + new_cap.capid, err); + BUG(); + } + } else /* Success */ + goto success; } + return -ENOCAP; -found: - /* Copy possessed one to new one's buffer */ - memcpy(&new_cap, possessed, sizeof(*possessed)); - - /* Replicate the possessed capability, giving original as reference */ - if ((err = l4_capability_control(CAP_CONTROL_REPLICATE, - 0, 0, 0, &new_cap)) < 0) { - printf("l4_capability_control() replication of " - "capability failed.\n Could not " - "complete CAP_CONTROL_REPLICATE request on cap (%d), " - "err = %d.\n", new_cap.capid, err); - return err; - } - +success: /* - * The returned capability is a replica. + * Found suitable one to replicate/deduce. + * Grant it to requested owner. * - * We don't add the newly created one to our own internal - * list because we will grant it shortly and lose its - * possession - * - * Now deduce it such that it looks like the one requested. - * Note, we assume the request had been validated before. - */ - new_cap.owner = cap->owner; - new_cap.resid = cap->resid; - new_cap.type = cap->type; - new_cap.access = cap->access; - new_cap.start = cap->start; - new_cap.end = cap->end; - new_cap.size = cap->size; - new_cap.used = cap->used; - - /* - * Make sure it is transferable, - * since we will need to grant it soon - */ - new_cap.access |= CAP_TRANSFERABLE; - - if ((err = l4_capability_control(CAP_CONTROL_DEDUCE, - 0, 0, 0, &new_cap)) < 0) { - printf("l4_capability_control() deduction of " - "ipc capability failed.\n Could not " - "complete CAP_CONTROL_DEDUCE request on cap (%d), " - "err = %d.\n", new_cap.capid, err); - return err; - } - - /* - * Grant it to given owner. - * - * This effectively enables the owner to have all operations defined - * in the capability. However, we use a flag to make the capability - * immutable as we grant it. (We wouldn't be able to grant it if it - * had no grant permission originally. We remove it as we grant it) + * This effectively enables the owner to have all + * operations defined in the capability. However, + * we use a flag to make the capability immutable + * as we grant it. (We wouldn't be able to grant + * it if it had no grant permission originally. We + * remove it _as_ we grant it) */ + new_cap.owner = cap->owner; /* Indicate new owner */ if ((err = l4_capability_control(CAP_CONTROL_GRANT, - CAP_GRANT_SINGLE | CAP_GRANT_IMMUTABLE, + CAP_GRANT_SINGLE | + CAP_GRANT_IMMUTABLE, 0, 0, &new_cap)) < 0) { printf("l4_capability_control() granting of " "capability (%d) failed.\n Could not " @@ -493,8 +506,7 @@ int sys_request_cap(struct tcb *task, struct capability *__cap_userptr) } out: - pager_unmap_user_range(__cap_userptr, - sizeof(*__cap_userptr)); + pager_unmap_user_range(cap, sizeof(*cap)); return ret; } diff --git a/conts/posix/test0/src/ipctest.c b/conts/posix/test0/src/ipctest.c index cb09614..bdd070a 100644 --- a/conts/posix/test0/src/ipctest.c +++ b/conts/posix/test0/src/ipctest.c @@ -105,11 +105,11 @@ void ipc_extended_test(void) cap.owner = parent; cap.resid = child; } else { - cap.owner = child; + cap.owner = getpid(); cap.resid = parent; } cap.type = CAP_TYPE_IPC | CAP_RTYPE_THREAD; - cap.access = CAP_IPC_EXTENDED; + cap.access = CAP_IPC_EXTENDED | CAP_IPC_SEND | CAP_IPC_RECV; if ((err = cap_request_pager(&cap)) < 0) { printf("Ipc capability request failed. " "err = %d\n", err); diff --git a/include/l4/api/capability.h b/include/l4/api/capability.h index 82b1ddb..0e56e41 100644 --- a/include/l4/api/capability.h +++ b/include/l4/api/capability.h @@ -14,6 +14,7 @@ #define CAP_CONTROL_REPLICATE 0x00000005 #define CAP_CONTROL_SPLIT 0x00000006 #define CAP_CONTROL_DEDUCE 0x00000007 +#define CAP_CONTROL_DESTROY 0x00000008 #define CAP_SHARE_MASK 0x00000003 #define CAP_SHARE_SINGLE 0x00000001 diff --git a/include/l4/generic/cap-types.h b/include/l4/generic/cap-types.h index 3fe411e..0afad80 100644 --- a/include/l4/generic/cap-types.h +++ b/include/l4/generic/cap-types.h @@ -108,6 +108,7 @@ #define CAP_CAP_REPLICATE (1 << 3) #define CAP_CAP_SPLIT (1 << 4) #define CAP_CAP_DEDUCE (1 << 5) -#define CAP_CAP_MODIFY (CAP_CAP_SPLIT | CAP_CAP_DEDUCE) - +#define CAP_CAP_DESTROY (1 << 6) +#define CAP_CAP_MODIFY (CAP_CAP_DEDUCE | CAP_CAP_SPLIT \ + | CAP_CAP_DESTROY) #endif /* __CAP_TYPES_H__ */ diff --git a/include/l4/generic/capability.h b/include/l4/generic/capability.h index cee582e..9bc5623 100644 --- a/include/l4/generic/capability.h +++ b/include/l4/generic/capability.h @@ -99,6 +99,13 @@ static inline void cap_list_insert(struct capability *cap, clist->ncaps++; } +static inline void cap_list_remove(struct capability *cap, + struct cap_list *clist) +{ + list_remove(&cap->list); + clist->ncaps--; +} + /* Detach a whole list of capabilities from list head */ static inline struct capability * cap_list_detach(struct cap_list *clist) @@ -141,6 +148,8 @@ struct capability *cap_list_find_by_rtype(struct cap_list *clist, unsigned int rtype); struct capability *cap_find_byid(l4id_t capid); +int cap_find_destroy(l4id_t capid); + /* Capability checking on system calls */ int cap_map_check(struct ktcb *task, unsigned long phys, unsigned long virt, unsigned long npages, unsigned int flags); diff --git a/src/api/cap.c b/src/api/cap.c index dce2c3a..f2c5ff0 100644 --- a/src/api/cap.c +++ b/src/api/cap.c @@ -97,13 +97,14 @@ int cap_share(l4id_t capid, unsigned int flags) } /* Grants all caps */ -int cap_grant_all(l4id_t tid, unsigned int flags) +int cap_grant_all(struct capability *req, unsigned int flags) { struct ktcb *target; struct capability *cap_head, *cap; int err; - if (!(target = tcb_find(tid))) + /* Owners are always threads, for simplicity */ + if (!(target = tcb_find(req->owner))) return -ESRCH; /* Detach all caps */ @@ -112,6 +113,7 @@ int cap_grant_all(l4id_t tid, unsigned int flags) list_foreach_struct(cap, &cap_head->list, list) { /* Change ownership */ cap->owner = target->tid; + BUG_ON(target->tid != req->owner); /* Make immutable if GRANT_IMMUTABLE given */ if (flags & CAP_GRANT_IMMUTABLE) { @@ -140,15 +142,15 @@ out_err: return err; } -int cap_grant_single(l4id_t capid, l4id_t tid, unsigned int flags) +int cap_grant_single(struct capability *req, unsigned int flags) { struct capability *cap; struct ktcb *target; - if (!(cap = cap_find_byid(capid))) + if (!(cap = cap_find_byid(req->capid))) return -EEXIST; - if (!(target = tcb_find(tid))) + if (!(target = tcb_find(req->owner))) return -ESRCH; if (cap->owner != current->tid) @@ -163,6 +165,7 @@ int cap_grant_single(l4id_t capid, l4id_t tid, unsigned int flags) /* Change ownership */ cap->owner = target->tid; + BUG_ON(cap->owner != req->owner); /* Make immutable if GRANT_IMMUTABLE given */ if (flags & CAP_GRANT_IMMUTABLE) { @@ -176,12 +179,12 @@ int cap_grant_single(l4id_t capid, l4id_t tid, unsigned int flags) return 0; } -int cap_grant(l4id_t capid, l4id_t tid, unsigned int flags) +int cap_grant(struct capability *cap, unsigned int flags) { if (flags & CAP_GRANT_SINGLE) - cap_grant_single(capid, tid, flags); + cap_grant_single(cap, flags); else if (flags & CAP_GRANT_ALL) - cap_grant_all(tid, flags); + cap_grant_all(cap, flags); else return -EINVAL; return 0; @@ -288,6 +291,10 @@ int cap_deduce(struct capability *new) if ((ret = cap_deduce_rtype(orig, new)) < 0) return ret; + /* Check owners are same for request validity */ + if (orig->owner != new->owner) + return -EINVAL; + /* Check permissions for deduction */ if (orig->access) { /* New cannot have more bits than original */ @@ -340,6 +347,29 @@ int cap_deduce(struct capability *new) return 0; } +/* + * Destroys a capability + */ +int cap_destroy(struct capability *cap) +{ + struct capability *orig; + + /* Find original capability */ + if (!(orig = cap_find_byid(cap->capid))) + return -EEXIST; + + /* Check that caller is owner */ + if (orig->owner != current->tid) + return -ENOCAP; + + /* Check that it is destroyable */ + if (!(cap_generic_perms(orig) & CAP_CHANGEABLE)) + return -ENOCAP; + + cap_find_destroy(cap->capid); + return 0; +} + /* * Splits a capability * @@ -374,6 +404,10 @@ int cap_split(struct capability *diff) if (orig->owner != current->tid) return -ENOCAP; + /* Check owners are same for request validity */ + if (orig->owner != diff->owner) + return -EINVAL; + /* Check that it is splitable */ if (!(orig->access & CAP_CHANGEABLE)) return -ENOCAP; @@ -427,6 +461,9 @@ int cap_split(struct capability *diff) } else if (new->size) return -EINVAL; + /* No used diff allowed for request validity */ + if (diff->used) + return -EINVAL; /* Check range usage but don't split if requested */ if (orig->start || orig->end) { @@ -439,8 +476,6 @@ int cap_split(struct capability *diff) } else if (new->start || new->end) return -EINVAL; - - /* Copy other fields */ new->type = orig->type; new->resid = orig->resid; @@ -557,14 +592,24 @@ int sys_capability_control(unsigned int req, unsigned int flags, err = cap_share(capid, flags); break; case CAP_CONTROL_GRANT: - err = cap_grant(flags, capid, target); + if ((err = check_access((unsigned long)userbuf, + sizeof(struct capability), + MAP_USR_RW_FLAGS, 1)) < 0) + return err; + + err = cap_grant((struct capability *)userbuf, flags); break; case CAP_CONTROL_SPLIT: + if ((err = check_access((unsigned long)userbuf, + sizeof(struct capability), + MAP_USR_RW_FLAGS, 1)) < 0) + return err; + err = cap_split((struct capability *)userbuf); break; case CAP_CONTROL_REPLICATE: if ((err = check_access((unsigned long)userbuf, - sizeof(int), + sizeof(struct capability), MAP_USR_RW_FLAGS, 1)) < 0) return err; @@ -572,12 +617,21 @@ int sys_capability_control(unsigned int req, unsigned int flags, break; case CAP_CONTROL_DEDUCE: if ((err = check_access((unsigned long)userbuf, - sizeof(int), + sizeof(struct capability), MAP_USR_RW_FLAGS, 1)) < 0) return err; err = cap_deduce((struct capability *)userbuf); break; + case CAP_CONTROL_DESTROY: + if ((err = check_access((unsigned long)userbuf, + sizeof(struct capability), + MAP_USR_RW_FLAGS, 1)) < 0) + return err; + + err = cap_destroy((struct capability *)userbuf); + break; + default: /* Invalid request id */ return -EINVAL; diff --git a/src/generic/capability.c b/src/generic/capability.c index 194d2f9..9800340 100644 --- a/src/generic/capability.c +++ b/src/generic/capability.c @@ -154,6 +154,43 @@ struct capability *cap_find_byid(l4id_t capid) return 0; } +/* + * TODO: Instead of destroying, use cap_find_byid match function's + * match_args to pass a pointer to the capability list, so the + * caller may destroy it + */ +int cap_find_destroy(l4id_t capid) +{ + struct capability *cap; + struct ktcb *task = current; + + /* Search task's own list */ + list_foreach_struct(cap, &task->cap_list.caps, list) + if (cap->capid == capid) { + cap_list_remove(cap, &task->cap_list); + free_capability(cap); + return 0; + } + + /* Search space list */ + list_foreach_struct(cap, &task->space->cap_list.caps, list) + if (cap->capid == capid) { + cap_list_remove(cap, &task->space->cap_list); + free_capability(cap); + return 0; + } + + /* Search container list */ + list_foreach_struct(cap, &task->container->cap_list.caps, list) + if (cap->capid == capid) { + cap_list_remove(cap, &task->container->cap_list); + free_capability(cap); + return 0; + } + + return -EEXIST; +} + typedef struct capability *(*cap_match_func_t) \ (struct capability *cap, void *match_args); @@ -273,6 +310,9 @@ cap_match_capctrl(struct capability *cap, void *args_ptr) if (req == CAP_CONTROL_DEDUCE) if (!(cap->access & CAP_CAP_DEDUCE)) return 0; + if (req == CAP_CONTROL_DESTROY) + if (!(cap->access & CAP_CAP_DESTROY)) + return 0; /* Now check the usual restype/resid pair */ switch (cap_rtype(cap)) {