Теперь я буду собирать все элементы мозаики в кучу. Отдельного внимания разве что стоит механизм, которым я буду разрешать ядру переключать uid/gid или дополнительные группы, если мои проверки прошли успешно, но процесс непривелигерован. План такой:
- Даю процессу (задаче) CAP_SETUID/CAP_SETGID
- Вызываю sys_setuid/sys_setgid/sys_setgroups
- Отбираю CAP_SETUID/CAP_SETGID. Если на этой стадии что-то пошло не по плану, убиваю процесс SIGKILL‘ом — ему нельзя позволить дальше работать с повышенными привилегиями
Примерно это должно выглядеть вот так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
static long ugidctl_setuid(uid_t uid) { struct cred *cred; long rc; cred = prepare_creds(); if (!cred) return -ENOMEM; cap_raise(cred->cap_effective, CAP_SETUID); commit_creds(cred); rc = ugidctl_sys_setuid(uid); cred = prepare_creds(); if (!cred) { do_exit(SIGKILL); return -ENOMEM; } cap_lower(cred->cap_effective, CAP_SETUID); commit_creds(cred); return rc; } |
Также хочу отдельно отметить, где буду хранить ключ и корни деревьев со списком разрешенных uid’ов и gid’ов для открытого файлового дескриптора. При отрытии устройства, kmalloc‘ом можно выделить небольшой кусок памяти, закинуть туда рандомный ключ, корни деревьев и всяких полезностей. Адрес на этот кусок памяти можно положить в поле private_data структуры file, которую ядро мне будет передавать при каждом вызове open, ioctl и close. При закрытии нужно будет освободить этот кусок памяти и все связанные с ним структуры:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
struct ugidctl_context { struct rb_root uids; struct rb_root gids; __u8 key[32]; } static int ugidctl_open(struct inode *inode, struct file *filp) { struct ugidctl_context *ctx; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; get_random_bytes(ctx->key, sizeof(ctx->key)); ctx->uids = RB_ROOT; ctx->uids = RB_ROOT; filp->private_data = ctx; return 0; } static int ugidctl_release(struct inode *inode, struct file *filp) { struct ugidctl_context *ctx = filp->private_data; // flush ctx->uids // flush ctx->gids; kfree(ctx); return 0; } static long ugidctl_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct ugidctl_context *ctx = filp->private_data; // do someting using ctx return -ENOIOCTLCMD; } |
Полный листинг получившегося модуля выйдет слишком большим, поэтому даю ссылки на результат.
Header-файл с описанием протокола и структур данных: ugidctl.h
Функции работы с красно-черными деревьями: ugidctl-cache.c
Основной модуль: ugidctl-dev.c