From 2bfc50bc4fbc3ad7e8bdfeb6b55d72ea55c4f18b Mon Sep 17 00:00:00 2001 From: Edward Tomasz Napierala Date: Sat, 5 Mar 2011 12:40:35 +0000 Subject: [PATCH] Add two new system calls, setloginclass(2) and getloginclass(2). This makes it possible for the kernel to track login class the process is assigned to, which is required for RCTL. This change also make setusercontext(3) call setloginclass(2) and makes it possible to retrieve current login class using id(1). Reviewed by: kib (as part of a larger patch) --- include/unistd.h | 2 + lib/libc/sys/Symbol.map | 2 + lib/libutil/login_cap.h | 3 +- lib/libutil/login_class.c | 23 +++ sys/compat/freebsd32/syscalls.master | 3 + sys/conf/files | 1 + sys/kern/init_main.c | 2 + sys/kern/kern_jail.c | 6 + sys/kern/kern_loginclass.c | 220 +++++++++++++++++++++++++++ sys/kern/kern_prot.c | 4 + sys/kern/syscalls.master | 3 + sys/sys/loginclass.h | 49 ++++++ sys/sys/priv.h | 1 + sys/sys/ucred.h | 4 +- usr.bin/id/id.1 | 6 +- usr.bin/id/id.c | 22 ++- 16 files changed, 344 insertions(+), 7 deletions(-) create mode 100644 sys/kern/kern_loginclass.c create mode 100644 sys/sys/loginclass.h diff --git a/include/unistd.h b/include/unistd.h index adbaa549ae1..f073f8da81c 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -500,6 +500,7 @@ int feature_present(const char *); char *fflagstostr(u_long); int getdomainname(char *, int); int getgrouplist(const char *, gid_t, gid_t *, int *); +int getloginclass(char *, size_t); mode_t getmode(const void *, mode_t); int getosreldate(void); int getpeereid(int, uid_t *, gid_t *); @@ -560,6 +561,7 @@ int setkey(const char *); #define _SETKEY_DECLARED #endif int setlogin(const char *); +int setloginclass(const char *); void *setmode(const char *); void setproctitle(const char *_fmt, ...) __printf0like(1, 2); int setresgid(gid_t, gid_t, gid_t); diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map index af9838a6e04..07e5ab688f2 100644 --- a/lib/libc/sys/Symbol.map +++ b/lib/libc/sys/Symbol.map @@ -344,6 +344,7 @@ FBSD_1.1 { fexecve; fstatat; futimesat; + getloginclass; jail_get; jail_set; jail_remove; @@ -357,6 +358,7 @@ FBSD_1.1 { readlinkat; renameat; setfib; + setloginclass; shmctl; symlinkat; unlinkat; diff --git a/lib/libutil/login_cap.h b/lib/libutil/login_cap.h index 082e34bc11a..ec1421b8066 100644 --- a/lib/libutil/login_cap.h +++ b/lib/libutil/login_cap.h @@ -49,7 +49,8 @@ #define LOGIN_SETENV 0x0080 /* set user environment */ #define LOGIN_SETMAC 0x0100 /* set user default MAC label */ #define LOGIN_SETCPUMASK 0x0200 /* set user cpumask */ -#define LOGIN_SETALL 0x03ff /* set everything */ +#define LOGIN_SETLOGINCLASS 0x0400 /* set login class in the kernel */ +#define LOGIN_SETALL 0x07ff /* set everything */ #define BI_AUTH "authorize" /* accepted authentication */ #define BI_REJECT "reject" /* rejected authentication */ diff --git a/lib/libutil/login_class.c b/lib/libutil/login_class.c index d952940f600..68fdf2b49ff 100644 --- a/lib/libutil/login_class.c +++ b/lib/libutil/login_class.c @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -425,6 +426,7 @@ setusercontext(login_cap_t *lc, const struct passwd *pwd, uid_t uid, unsigned in quad_t p; mode_t mymask; login_cap_t *llc = NULL; + struct sigaction sa, prevsa; struct rtprio rtp; int error; @@ -512,6 +514,27 @@ setusercontext(login_cap_t *lc, const struct passwd *pwd, uid_t uid, unsigned in return (-1); } + /* Inform the kernel about current login class */ + if (lc != NULL && lc->lc_class != NULL && (flags & LOGIN_SETLOGINCLASS)) { + /* + * XXX: This is a workaround to fail gracefully in case the kernel + * does not support setloginclass(2). + */ + bzero(&sa, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sigfillset(&sa.sa_mask); + sigaction(SIGSYS, &sa, &prevsa); + error = setloginclass(lc->lc_class); + sigaction(SIGSYS, &prevsa, NULL); + if (error != 0) { + syslog(LOG_ERR, "setloginclass(%s): %m", lc->lc_class); +#ifdef notyet + login_close(llc); + return (-1); +#endif + } + } + mymask = (flags & LOGIN_SETUMASK) ? umask(LOGIN_DEFUMASK) : 0; mymask = setlogincontext(lc, pwd, mymask, flags); login_close(llc); diff --git a/sys/compat/freebsd32/syscalls.master b/sys/compat/freebsd32/syscalls.master index 4aa8d3e4c99..26c0f6e5569 100644 --- a/sys/compat/freebsd32/syscalls.master +++ b/sys/compat/freebsd32/syscalls.master @@ -962,3 +962,6 @@ fd_set *ou, fd_set *ex, \ const struct timespec32 *ts, \ const sigset_t *sm); } +523 AUE_NULL NOPROTO { int getloginclass(char *namebuf, \ + size_t namelen); } +524 AUE_NULL NOPROTO { int setloginclass(const char *namebuf); } diff --git a/sys/conf/files b/sys/conf/files index 96b21b0e2dd..735503ddea3 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -2190,6 +2190,7 @@ kern/kern_linker.c standard kern/kern_lock.c standard kern/kern_lockf.c standard kern/kern_lockstat.c optional kdtrace_hooks +kern/kern_loginclass.c standard kern/kern_malloc.c standard kern/kern_mbuf.c standard kern/kern_mib.c standard diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index f472c829ec6..9250e79c36e 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -55,6 +55,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -484,6 +485,7 @@ proc0_init(void *dummy __unused) p->p_ucred->cr_uidinfo = uifind(0); p->p_ucred->cr_ruidinfo = uifind(0); p->p_ucred->cr_prison = &prison0; + p->p_ucred->cr_loginclass = loginclass_find("default"); #ifdef AUDIT audit_cred_kproc0(p->p_ucred); #endif diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index 335cd31e8e1..ed98a77d7a7 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -3874,6 +3874,12 @@ prison_priv_check(struct ucred *cred, int priv) case PRIV_NETINET_GETCRED: return (0); + /* + * Allow jailed root to set loginclass. + */ + case PRIV_PROC_SETLOGINCLASS: + return (0); + default: /* * In all remaining cases, deny the privilege request. This diff --git a/sys/kern/kern_loginclass.c b/sys/kern/kern_loginclass.c new file mode 100644 index 00000000000..cf644d525e0 --- /dev/null +++ b/sys/kern/kern_loginclass.c @@ -0,0 +1,220 @@ +/*- + * Copyright (c) 2011 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Processes may set login class name using setloginclass(2). This + * is usually done through call to setusercontext(3), by programs + * such as login(1), based on information from master.passwd(5). Kernel + * uses this information to enforce per-class resource limits. Current + * login class can be determined using id(1). Login class is inherited + * from the parent process during fork(2). If not set, it defaults + * to "default". + * + * Code in this file implements setloginclass(2) and getloginclass(2) + * system calls, and maintains class name storage and retrieval. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static MALLOC_DEFINE(M_LOGINCLASS, "loginclass", "loginclass structures"); + +LIST_HEAD(, loginclass) loginclasses; + +/* + * Lock protecting loginclasses list. + */ +static struct mtx loginclasses_lock; + +static void lc_init(void); +SYSINIT(loginclass, SI_SUB_CPU, SI_ORDER_FIRST, lc_init, NULL); + +void +loginclass_hold(struct loginclass *lc) +{ + + refcount_acquire(&lc->lc_refcount); +} + +void +loginclass_free(struct loginclass *lc) +{ + int old; + + old = lc->lc_refcount; + if (old > 1 && atomic_cmpset_int(&lc->lc_refcount, old, old - 1)) + return; + + mtx_lock(&loginclasses_lock); + if (refcount_release(&lc->lc_refcount)) { + LIST_REMOVE(lc, lc_next); + mtx_unlock(&loginclasses_lock); + free(lc, M_LOGINCLASS); + + return; + } + mtx_unlock(&loginclasses_lock); +} + +/* + * Return loginclass structure with a corresponding name. Not + * performance critical, as it's used mainly by setloginclass(2), + * which happens once per login session. Caller has to use + * loginclass_free() on the returned value when it's no longer + * needed. + */ +struct loginclass * +loginclass_find(const char *name) +{ + struct loginclass *lc, *newlc; + + if (name[0] == '\0' || strlen(name) >= MAXLOGNAME) + return (NULL); + + newlc = malloc(sizeof(*newlc), M_LOGINCLASS, M_ZERO | M_WAITOK); + + mtx_lock(&loginclasses_lock); + LIST_FOREACH(lc, &loginclasses, lc_next) { + if (strcmp(name, lc->lc_name) != 0) + continue; + + /* Found loginclass with a matching name? */ + loginclass_hold(lc); + mtx_unlock(&loginclasses_lock); + free(newlc, M_LOGINCLASS); + return (lc); + } + + /* Add new loginclass. */ + strcpy(newlc->lc_name, name); + refcount_init(&newlc->lc_refcount, 1); + LIST_INSERT_HEAD(&loginclasses, newlc, lc_next); + mtx_unlock(&loginclasses_lock); + + return (newlc); +} + +/* + * Get login class name. + */ +#ifndef _SYS_SYSPROTO_H_ +struct getloginclass_args { + char *namebuf; + size_t namelen; +}; +#endif +/* ARGSUSED */ +int +getloginclass(struct thread *td, struct getloginclass_args *uap) +{ + int error = 0; + size_t lcnamelen; + struct proc *p; + struct loginclass *lc; + + p = td->td_proc; + PROC_LOCK(p); + lc = p->p_ucred->cr_loginclass; + loginclass_hold(lc); + PROC_UNLOCK(p); + + lcnamelen = strlen(lc->lc_name) + 1; + if (lcnamelen > uap->namelen) + error = ERANGE; + if (error == 0) + error = copyout(lc->lc_name, uap->namebuf, lcnamelen); + loginclass_free(lc); + return (error); +} + +/* + * Set login class name. + */ +#ifndef _SYS_SYSPROTO_H_ +struct setloginclass_args { + const char *namebuf; +}; +#endif +/* ARGSUSED */ +int +setloginclass(struct thread *td, struct setloginclass_args *uap) +{ + struct proc *p = td->td_proc; + int error; + char lcname[MAXLOGNAME]; + struct loginclass *newlc; + struct ucred *newcred, *oldcred; + + error = priv_check(td, PRIV_PROC_SETLOGINCLASS); + if (error != 0) + return (error); + error = copyinstr(uap->namebuf, lcname, sizeof(lcname), NULL); + if (error != 0) + return (error); + + newlc = loginclass_find(lcname); + if (newlc == NULL) + return (EINVAL); + newcred = crget(); + + PROC_LOCK(p); + oldcred = crcopysafe(p, newcred); + newcred->cr_loginclass = newlc; + p->p_ucred = newcred; + PROC_UNLOCK(p); + + loginclass_free(oldcred->cr_loginclass); + crfree(oldcred); + + return (0); +} + +static void +lc_init(void) +{ + + mtx_init(&loginclasses_lock, "loginclasses lock", NULL, MTX_DEF); +} diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c index 4506034ab8c..9b94825aa26 100644 --- a/sys/kern/kern_prot.c +++ b/sys/kern/kern_prot.c @@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -1842,6 +1843,8 @@ crfree(struct ucred *cr) */ if (cr->cr_prison != NULL) prison_free(cr->cr_prison); + if (cr->cr_loginclass != NULL) + loginclass_free(cr->cr_loginclass); #ifdef AUDIT audit_cred_destroy(cr); #endif @@ -1878,6 +1881,7 @@ crcopy(struct ucred *dest, struct ucred *src) uihold(dest->cr_uidinfo); uihold(dest->cr_ruidinfo); prison_hold(dest->cr_prison); + loginclass_hold(dest->cr_loginclass); #ifdef AUDIT audit_cred_copy(src, dest); #endif diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index f3723e3dbf2..b204254f947 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -926,5 +926,8 @@ fd_set *ou, fd_set *ex, \ const struct timespec *ts, \ const sigset_t *sm); } +523 AUE_NULL STD { int getloginclass(char *namebuf, \ + size_t namelen); } +524 AUE_NULL STD { int setloginclass(const char *namebuf); } ; Please copy any additions and changes to the following compatability tables: ; sys/compat/freebsd32/syscalls.master diff --git a/sys/sys/loginclass.h b/sys/sys/loginclass.h new file mode 100644 index 00000000000..36ecf809f1a --- /dev/null +++ b/sys/sys/loginclass.h @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 2011 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SYS_LOGINCLASS_H_ +#define _SYS_LOGINCLASS_H_ + +/* + * Exactly one of these structures exists per login class. + */ +struct loginclass { + LIST_ENTRY(loginclass) lc_next; + char lc_name[MAXLOGNAME]; + u_int lc_refcount; +}; + +void loginclass_hold(struct loginclass *lc); +void loginclass_free(struct loginclass *lc); +struct loginclass *loginclass_find(const char *name); + +#endif /* !_SYS_LOGINCLASS_H_ */ + diff --git a/sys/sys/priv.h b/sys/sys/priv.h index 44d1d9422b0..5383d581778 100644 --- a/sys/sys/priv.h +++ b/sys/sys/priv.h @@ -156,6 +156,7 @@ #define PRIV_PROC_LIMIT 160 /* Exceed user process limit. */ #define PRIV_PROC_SETLOGIN 161 /* Can call setlogin. */ #define PRIV_PROC_SETRLIMIT 162 /* Can raise resources limits. */ +#define PRIV_PROC_SETLOGINCLASS 163 /* Can call setloginclass(2). */ /* System V IPC privileges. */ diff --git a/sys/sys/ucred.h b/sys/sys/ucred.h index 4e2ca02b630..e1648d4b364 100644 --- a/sys/sys/ucred.h +++ b/sys/sys/ucred.h @@ -35,6 +35,8 @@ #include +struct loginclass; + /* * Credentials. * @@ -54,7 +56,7 @@ struct ucred { struct uidinfo *cr_uidinfo; /* per euid resource consumption */ struct uidinfo *cr_ruidinfo; /* per ruid resource consumption */ struct prison *cr_prison; /* jail(2) */ - void *cr_pspare; /* general use */ + struct loginclass *cr_loginclass; /* login class */ u_int cr_flags; /* credential flags */ void *cr_pspare2[2]; /* general use 2 */ #define cr_endcopy cr_label diff --git a/usr.bin/id/id.1 b/usr.bin/id/id.1 index 7b122bb7d28..b69767830c4 100644 --- a/usr.bin/id/id.1 +++ b/usr.bin/id/id.1 @@ -31,7 +31,7 @@ .\" @(#)id.1 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" -.Dd September 26, 2006 +.Dd March 5, 2011 .Dt ID 1 .Os .Sh NAME @@ -51,6 +51,8 @@ .Fl P .Op Ar user .Nm +.Fl c +.Nm .Fl g Op Fl nr .Op Ar user .Nm @@ -89,6 +91,8 @@ Display the id as a password file entry. Ignored for compatibility with other .Nm implementations. +.It Fl c +Display current login class. .It Fl g Display the effective group ID as a number. .It Fl n diff --git a/usr.bin/id/id.c b/usr.bin/id/id.c index e3e8f4c6d13..e35271288de 100644 --- a/usr.bin/id/id.c +++ b/usr.bin/id/id.c @@ -74,11 +74,13 @@ main(int argc, char *argv[]) struct group *gr; struct passwd *pw; int Gflag, Mflag, Pflag, ch, gflag, id, nflag, pflag, rflag, uflag; - int Aflag; + int Aflag, cflag; + int error; const char *myname; + char loginclass[MAXLOGNAME]; Gflag = Mflag = Pflag = gflag = nflag = pflag = rflag = uflag = 0; - Aflag = 0; + Aflag = cflag = 0; myname = strrchr(argv[0], '/'); myname = (myname != NULL) ? myname + 1 : argv[0]; @@ -92,7 +94,7 @@ main(int argc, char *argv[]) } while ((ch = getopt(argc, argv, - (isgroups || iswhoami) ? "" : "APGMagnpru")) != -1) + (isgroups || iswhoami) ? "" : "APGMacgnpru")) != -1) switch(ch) { #ifdef USE_BSM_AUDIT case 'A': @@ -110,6 +112,9 @@ main(int argc, char *argv[]) break; case 'a': break; + case 'c': + cflag = 1; + break; case 'g': gflag = 1; break; @@ -158,6 +163,14 @@ main(int argc, char *argv[]) } #endif + if (cflag) { + error = getloginclass(loginclass, sizeof(loginclass)); + if (error != 0) + err(1, "loginclass"); + (void)printf("%s\n", loginclass); + exit(0); + } + if (gflag) { id = pw ? pw->pw_gid : rflag ? getgid() : getegid(); if (nflag && (gr = getgrgid(id))) @@ -467,7 +480,7 @@ usage(void) else if (iswhoami) (void)fprintf(stderr, "usage: whoami\n"); else - (void)fprintf(stderr, "%s\n%s%s\n%s\n%s\n%s\n%s\n%s\n", + (void)fprintf(stderr, "%s\n%s%s\n%s\n%s\n%s\n%s\n%s\n%s\n", "usage: id [user]", #ifdef USE_BSM_AUDIT " id -A\n", @@ -477,6 +490,7 @@ usage(void) " id -G [-n] [user]", " id -M", " id -P [user]", + " id -c", " id -g [-nr] [user]", " id -p [user]", " id -u [-nr] [user]");