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]");