Rework source layout in preparation for modular sudo.

This commit is contained in:
Todd C. Miller
2010-02-20 09:14:01 -05:00
parent 28c24027ec
commit e90fa482f9
151 changed files with 0 additions and 0 deletions

127
src/aix.c Normal file
View File

@@ -0,0 +1,127 @@
/*
* Copyright (c) 2008 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#include <usersec.h>
#include <compat.h>
#ifdef HAVE_GETUSERATTR
#ifndef HAVE_SETRLIMIT64
# define setrlimit64(a, b) setrlimit(a, b)
# define rlimit64 rlimit
# define rlim64_t rlim_t
# define RLIM64_INFINITY RLIM_INFINITY
#endif /* HAVE_SETRLIMIT64 */
#ifndef RLIM_SAVED_MAX
# define RLIM_SAVED_MAX RLIM64_INFINITY
#endif
struct aix_limit {
int resource;
char *soft;
char *hard;
int factor;
};
static struct aix_limit aix_limits[] = {
{ RLIMIT_FSIZE, S_UFSIZE, S_UFSIZE_HARD, 512 },
{ RLIMIT_CPU, S_UCPU, S_UCPU_HARD, 1 },
{ RLIMIT_DATA, S_UDATA, S_UDATA_HARD, 512 },
{ RLIMIT_STACK, S_USTACK, S_USTACK_HARD, 512 },
{ RLIMIT_RSS, S_URSS, S_URSS_HARD, 512 },
{ RLIMIT_CORE, S_UCORE, S_UCORE_HARD, 512 },
{ RLIMIT_NOFILE, S_UNOFILE, S_UNOFILE_HARD, 1 }
};
static int
aix_getlimit(user, lim, valp)
char *user;
char *lim;
rlim64_t *valp;
{
int val;
if (getuserattr(user, lim, &val, SEC_INT) != 0 &&
getuserattr("default", lim, &val, SEC_INT) != 0) {
return(-1);
}
*valp = val;
return(0);
}
void
aix_setlimits(user)
char *user;
{
struct rlimit64 rlim;
rlim64_t val;
int n;
/*
* For each resource limit, get the soft/hard values for the user
* and set those values via setrlimit64(). Must be run as euid 0.
*/
for (n = 0; n < sizeof(aix_limits) / sizeof(aix_limits[0]); n++) {
/*
* We have two strategies, depending on whether or not the
* hard limit has been defined.
*/
if (aix_getlimit(user, aix_limits[n].hard, &val) == 0) {
rlim.rlim_max = val == -1 ? RLIM64_INFINITY : val * aix_limits[n].factor;
if (aix_getlimit(user, aix_limits[n].soft, &val) == 0)
rlim.rlim_cur = val == -1 ? RLIM64_INFINITY : val * aix_limits[n].factor;
else
rlim.rlim_cur = rlim.rlim_max; /* soft not specd, use hard */
} else {
/* No hard limit set, try soft limit. */
if (aix_getlimit(user, aix_limits[n].soft, &val) == 0)
rlim.rlim_cur = val == -1 ? RLIM64_INFINITY : val * aix_limits[n].factor;
/* Set hard limit per AIX /etc/security/limits documentation. */
switch (aix_limits[n].resource) {
case RLIMIT_CPU:
case RLIMIT_FSIZE:
rlim.rlim_max = rlim.rlim_cur;
break;
case RLIMIT_STACK:
rlim.rlim_max = RLIM_SAVED_MAX;
break;
default:
rlim.rlim_max = RLIM64_INFINITY;
break;
}
}
(void)setrlimit64(aix_limits[n].resource, &rlim);
}
}
#endif /* HAVE_GETUSERATTR */

224
src/alloc.c Normal file
View File

@@ -0,0 +1,224 @@
/*
* Copyright (c) 1999-2005, 2007
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
# include <malloc.h>
#endif /* HAVE_MALLOC_H && !STDC_HEADERS */
#ifdef HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#include "sudo.h"
/*
* If there is no SIZE_MAX or SIZE_T_MAX we have to assume that size_t
* could be signed (as it is on SunOS 4.x). This just means that
* emalloc2() and erealloc3() cannot allocate huge amounts on such a
* platform but that is OK since sudo doesn't need to do so anyway.
*/
#ifndef SIZE_MAX
# ifdef SIZE_T_MAX
# define SIZE_MAX SIZE_T_MAX
# else
# define SIZE_MAX INT_MAX
# endif /* SIZE_T_MAX */
#endif /* SIZE_MAX */
/*
* emalloc() calls the system malloc(3) and exits with an error if
* malloc(3) fails.
*/
void *
emalloc(size)
size_t size;
{
void *ptr;
if (size == 0)
errorx(1, "internal error, tried to emalloc(0)");
if ((ptr = malloc(size)) == NULL)
errorx(1, "unable to allocate memory");
return(ptr);
}
/*
* emalloc2() allocates nmemb * size bytes and exits with an error
* if overflow would occur or if the system malloc(3) fails.
*/
void *
emalloc2(nmemb, size)
size_t nmemb;
size_t size;
{
void *ptr;
if (nmemb == 0 || size == 0)
errorx(1, "internal error, tried to emalloc2(0)");
if (nmemb > SIZE_MAX / size)
errorx(1, "internal error, emalloc2() overflow");
size *= nmemb;
if ((ptr = malloc(size)) == NULL)
errorx(1, "unable to allocate memory");
return(ptr);
}
/*
* erealloc() calls the system realloc(3) and exits with an error if
* realloc(3) fails. You can call erealloc() with a NULL pointer even
* if the system realloc(3) does not support this.
*/
void *
erealloc(ptr, size)
void *ptr;
size_t size;
{
if (size == 0)
errorx(1, "internal error, tried to erealloc(0)");
ptr = ptr ? realloc(ptr, size) : malloc(size);
if (ptr == NULL)
errorx(1, "unable to allocate memory");
return(ptr);
}
/*
* erealloc3() realloc(3)s nmemb * size bytes and exits with an error
* if overflow would occur or if the system malloc(3)/realloc(3) fails.
* You can call erealloc() with a NULL pointer even if the system realloc(3)
* does not support this.
*/
void *
erealloc3(ptr, nmemb, size)
void *ptr;
size_t nmemb;
size_t size;
{
if (nmemb == 0 || size == 0)
errorx(1, "internal error, tried to erealloc3(0)");
if (nmemb > SIZE_MAX / size)
errorx(1, "internal error, erealloc3() overflow");
size *= nmemb;
ptr = ptr ? realloc(ptr, size) : malloc(size);
if (ptr == NULL)
errorx(1, "unable to allocate memory");
return(ptr);
}
/*
* estrdup() is like strdup(3) except that it exits with an error if
* malloc(3) fails. NOTE: unlike strdup(3), estrdup(NULL) is legal.
*/
char *
estrdup(src)
const char *src;
{
char *dst = NULL;
size_t size;
if (src != NULL) {
size = strlen(src) + 1;
dst = (char *) emalloc(size);
(void) memcpy(dst, src, size);
}
return(dst);
}
/*
* easprintf() calls vasprintf() and exits with an error if vasprintf()
* returns -1 (out of memory).
*/
int
#ifdef __STDC__
easprintf(char **ret, const char *fmt, ...)
#else
easprintf(ret, fmt, va_alist)
char **ret;
const char *fmt;
va_dcl
#endif
{
int len;
va_list ap;
#ifdef __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
len = vasprintf(ret, fmt, ap);
va_end(ap);
if (len == -1)
errorx(1, "unable to allocate memory");
return(len);
}
/*
* evasprintf() calls vasprintf() and exits with an error if vasprintf()
* returns -1 (out of memory).
*/
int
evasprintf(ret, format, args)
char **ret;
const char *format;
va_list args;
{
int len;
if ((len = vasprintf(ret, format, args)) == -1)
errorx(1, "unable to allocate memory");
return(len);
}
/*
* Wrapper for free(3) so we can depend on C89 semantics.
*/
void
efree(ptr)
void *ptr;
{
if (ptr != NULL)
free(ptr);
}

76
src/audit.c Normal file
View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/types.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef __STDC__
# include <stdarg.h>
#else
# include <varargs.h>
#endif
#include "compat.h"
#include "logging.h"
#ifdef HAVE_BSM_AUDIT
# include "bsm_audit.h"
#endif
void
#ifdef __STDC__
audit_success(char **exec_args)
#else
audit_success(exec_args)
const char **exec_args;
#endif
{
#ifdef HAVE_BSM_AUDIT
bsm_audit_success(exec_args);
#endif
}
void
#ifdef __STDC__
audit_failure(char **exec_args, char const *const fmt, ...)
#else
audit_failure(exec_args, fmt, va_alist)
const char **exec_args;
char const *const fmt;
va_dcl
#endif
{
va_list ap;
#ifdef __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
#ifdef HAVE_BSM_AUDIT
bsm_audit_failure(exec_args, fmt, ap);
#endif
va_end(ap);
}

173
src/bsm_audit.c Normal file
View File

@@ -0,0 +1,173 @@
/*
* Copyright (c) 2009 Christian S.J. Peron
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/types.h>
#include <bsm/audit.h>
#include <bsm/libbsm.h>
#include <bsm/audit_uevents.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <pwd.h>
#include <errno.h>
#include <unistd.h>
void log_error(int flags, const char *fmt, ...) __attribute__((__noreturn__));
static int
audit_sudo_selected(int sf)
{
auditinfo_addr_t ainfo_addr;
struct au_mask *mask;
auditinfo_t ainfo;
int rc, sorf;
if (getaudit_addr(&ainfo_addr, sizeof(ainfo_addr)) < 0) {
if (errno == ENOSYS) {
if (getaudit(&ainfo) < 0)
log_error(0, "getaudit: failed");
mask = &ainfo.ai_mask;
} else
log_error(0, "getaudit: failed");
} else
mask = &ainfo_addr.ai_mask;
sorf = (sf == 0) ? AU_PRS_SUCCESS : AU_PRS_FAILURE;
rc = au_preselect(AUE_sudo, mask, sorf, AU_PRS_REREAD);
return (rc);
}
void
bsm_audit_success(char **exec_args)
{
auditinfo_addr_t ainfo_addr;
auditinfo_t ainfo;
token_t *tok;
au_id_t auid;
long au_cond;
int aufd;
pid_t pid;
pid = getpid();
/*
* If we are not auditing, don't cut an audit record; just return.
*/
if (auditon(A_GETCOND, (caddr_t)&au_cond, sizeof(long)) < 0) {
if (errno == ENOSYS)
return;
log_error(0, "Could not determine audit condition");
}
if (au_cond == AUC_NOAUDIT)
return;
/*
* Check to see if the preselection masks are interested in seeing
* this event.
*/
if (!audit_sudo_selected(0))
return;
if (getauid(&auid) < 0)
log_error(0, "getauid failed");
if ((aufd = au_open()) == -1)
log_error(0, "au_open: failed");
if (getaudit_addr(&ainfo_addr, sizeof(ainfo_addr)) == 0) {
tok = au_to_subject_ex(auid, geteuid(), getegid(), getuid(),
getuid(), pid, pid, &ainfo_addr.ai_termid);
} else if (errno == ENOSYS) {
/*
* NB: We should probably watch out for ERANGE here.
*/
if (getaudit(&ainfo) < 0)
log_error(0, "getaudit: failed");
tok = au_to_subject(auid, geteuid(), getegid(), getuid(),
getuid(), pid, pid, &ainfo.ai_termid);
} else
log_error(0, "getaudit: failed");
if (tok == NULL)
log_error(0, "au_to_subject: failed");
au_write(aufd, tok);
tok = au_to_exec_args(exec_args);
if (tok == NULL)
log_error(0, "au_to_exec_args: failed");
au_write(aufd, tok);
tok = au_to_return32(0, 0);
if (tok == NULL)
log_error(0, "au_to_return32: failed");
au_write(aufd, tok);
if (au_close(aufd, 1, AUE_sudo) == -1)
log_error(0, "unable to commit audit record");
}
void
bsm_audit_failure(char **exec_args, char const *const fmt, va_list ap)
{
auditinfo_addr_t ainfo_addr;
auditinfo_t ainfo;
char text[256];
token_t *tok;
long au_cond;
au_id_t auid;
pid_t pid;
int aufd;
pid = getpid();
/*
* If we are not auditing, don't cut an audit record; just return.
*/
if (auditon(A_GETCOND, &au_cond, sizeof(long)) < 0) {
if (errno == ENOSYS)
return;
log_error(0, "Could not determine audit condition");
}
if (au_cond == AUC_NOAUDIT)
return;
if (!audit_sudo_selected(1))
return;
if (getauid(&auid) < 0)
log_error(0, "getauid: failed");
if ((aufd = au_open()) == -1)
log_error(0, "au_open: failed");
if (getaudit_addr(&ainfo_addr, sizeof(ainfo_addr)) == 0) {
tok = au_to_subject_ex(auid, geteuid(), getegid(), getuid(),
getuid(), pid, pid, &ainfo_addr.ai_termid);
} else if (errno == ENOSYS) {
if (getaudit(&ainfo) < 0)
log_error(0, "getaudit: failed");
tok = au_to_subject(auid, geteuid(), getegid(), getuid(),
getuid(), pid, pid, &ainfo.ai_termid);
} else
log_error(0, "getaudit: failed");
if (tok == NULL)
log_error(0, "au_to_subject: failed");
au_write(aufd, tok);
tok = au_to_exec_args(exec_args);
if (tok == NULL)
log_error(0, "au_to_exec_args: failed");
au_write(aufd, tok);
(void) vsnprintf(text, sizeof(text), fmt, ap);
tok = au_to_text(text);
if (tok == NULL)
log_error(0, "au_to_text: failed");
au_write(aufd, tok);
tok = au_to_return32(EPERM, 1);
if (tok == NULL)
log_error(0, "au_to_return32: failed");
au_write(aufd, tok);
if (au_close(aufd, 1, AUE_sudo) == -1)
log_error(0, "unable to commit audit record");
}

23
src/bsm_audit.h Normal file
View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2009 Christian S.J. Peron
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _SUDO_BSM_AUDIT_H
#define _SUDO_BSM_AUDIT_H
void bsm_audit_success(char **);
void bsm_audit_failure(char **, char const * const, va_list);
#endif /* _SUDO_BSM_AUDIT_H */

129
src/error.c Normal file
View File

@@ -0,0 +1,129 @@
/*
* Copyright (c) 2004-2005 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <config.h>
#include <compat.h>
#include "error.h"
static void _warning __P((int, const char *, va_list));
void cleanup __P((int));
void
#ifdef __STDC__
error(int eval, const char *fmt, ...)
#else
error(eval, fmt, va_alist)
int eval;
const char *fmt;
va_dcl
#endif
{
va_list ap;
#ifdef __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
_warning(1, fmt, ap);
va_end(ap);
cleanup(0);
exit(eval);
}
void
#ifdef __STDC__
errorx(int eval, const char *fmt, ...)
#else
errorx(eval, fmt, va_alist)
int eval;
const char *fmt;
va_dcl
#endif
{
va_list ap;
#ifdef __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
_warning(0, fmt, ap);
va_end(ap);
cleanup(0);
exit(eval);
}
void
#ifdef __STDC__
warning(const char *fmt, ...)
#else
warning(fmt, va_alist)
const char *fmt;
va_dcl
#endif
{
va_list ap;
#ifdef __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
_warning(1, fmt, ap);
va_end(ap);
}
void
#ifdef __STDC__
warningx(const char *fmt, ...)
#else
warningx(fmt, va_alist)
const char *fmt;
va_dcl
#endif
{
va_list ap;
#ifdef __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
_warning(0, fmt, ap);
va_end(ap);
}
static void
_warning(use_errno, fmt, ap)
int use_errno;
const char *fmt;
va_list ap;
{
int serrno = errno;
fputs(getprogname(), stderr);
if (fmt != NULL) {
fputs(": ", stderr);
vfprintf(stderr, fmt, ap);
}
if (use_errno) {
fputs(": ", stderr);
fputs(strerror(serrno), stderr);
}
putc('\n', stderr);
}

177
src/fileops.c Normal file
View File

@@ -0,0 +1,177 @@
/*
* Copyright (c) 1999-2005,2007,2009 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#ifdef HAVE_FLOCK
# include <sys/file.h>
#endif /* HAVE_FLOCK */
#include <stdio.h>
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#include <ctype.h>
#include <limits.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <fcntl.h>
#if TIME_WITH_SYS_TIME
# include <time.h>
#endif
#ifndef HAVE_TIMESPEC
# include <emul/timespec.h>
#endif
#include "sudo.h"
#ifndef LINE_MAX
# define LINE_MAX 2048
#endif
/*
* Update the access and modify times on an fd or file.
*/
int
touch(fd, path, tsp)
int fd;
char *path;
struct timespec *tsp;
{
struct timeval times[2];
if (tsp != NULL) {
times[0].tv_sec = times[1].tv_sec = tsp->tv_sec;
times[0].tv_usec = times[1].tv_usec = tsp->tv_nsec / 1000;
}
#if defined(HAVE_FUTIME) || defined(HAVE_FUTIMES)
if (fd != -1)
return(futimes(fd, tsp ? times : NULL));
else
#endif
if (path != NULL)
return(utimes(path, tsp ? times : NULL));
else
return(-1);
}
/*
* Lock/unlock a file.
*/
#ifdef HAVE_LOCKF
int
lock_file(fd, lockit)
int fd;
int lockit;
{
int op = 0;
switch (lockit) {
case SUDO_LOCK:
op = F_LOCK;
break;
case SUDO_TLOCK:
op = F_TLOCK;
break;
case SUDO_UNLOCK:
op = F_ULOCK;
break;
}
return(lockf(fd, op, 0) == 0);
}
#elif HAVE_FLOCK
int
lock_file(fd, lockit)
int fd;
int lockit;
{
int op = 0;
switch (lockit) {
case SUDO_LOCK:
op = LOCK_EX;
break;
case SUDO_TLOCK:
op = LOCK_EX | LOCK_NB;
break;
case SUDO_UNLOCK:
op = LOCK_UN;
break;
}
return(flock(fd, op) == 0);
}
#else
int
lock_file(fd, lockit)
int fd;
int lockit;
{
#ifdef F_SETLK
int func;
struct flock lock;
lock.l_start = 0;
lock.l_len = 0;
lock.l_pid = getpid();
lock.l_type = (lockit == SUDO_UNLOCK) ? F_UNLCK : F_WRLCK;
lock.l_whence = SEEK_SET;
func = (lockit == SUDO_LOCK) ? F_SETLKW : F_SETLK;
return(fcntl(fd, func, &lock) == 0);
#else
return(TRUE);
#endif
}
#endif
/*
* Read a line of input, remove comments and strip off leading
* and trailing spaces. Returns static storage that is reused.
*/
char *
sudo_parseln(fp)
FILE *fp;
{
size_t len;
char *cp = NULL;
static char buf[LINE_MAX];
if (fgets(buf, sizeof(buf), fp) != NULL) {
/* Remove comments */
if ((cp = strchr(buf, '#')) != NULL)
*cp = '\0';
/* Trim leading and trailing whitespace/newline */
len = strlen(buf);
while (len > 0 && isspace((unsigned char)buf[len - 1]))
buf[--len] = '\0';
for (cp = buf; isblank(*cp); cp++)
continue;
}
return(cp);
}

285
src/lbuf.c Normal file
View File

@@ -0,0 +1,285 @@
/*
* Copyright (c) 2007-2009 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <ctype.h>
#ifdef HAVE_TERMIOS_H
# include <termios.h>
#else
# ifdef HAVE_TERMIO_H
# include <termio.h>
# endif
#endif
#include "sudo.h"
#include "lbuf.h"
#if !defined(TIOCGSIZE) && defined(TIOCGWINSZ)
# define TIOCGSIZE TIOCGWINSZ
# define ttysize winsize
# define ts_cols ws_col
#endif
int
get_ttycols()
{
char *p;
int cols;
#ifdef TIOCGSIZE
struct ttysize win;
if (ioctl(STDERR_FILENO, TIOCGSIZE, &win) == 0 && win.ts_cols != 0)
return((int)win.ts_cols);
#endif
/* Fall back on $COLUMNS. */
if ((p = getenv("COLUMNS")) == NULL || (cols = atoi(p)) <= 0)
cols = 80;
return(cols);
}
/*
* TODO: add support for embedded newlines in lbufs
*/
void
lbuf_init(lbuf, buf, indent, continuation)
struct lbuf *lbuf;
char *buf;
int indent;
int continuation;
{
lbuf->continuation = continuation;
lbuf->indent = indent;
lbuf->len = 0;
lbuf->size = 0;
lbuf->buf = NULL;
}
void
lbuf_destroy(lbuf)
struct lbuf *lbuf;
{
efree(lbuf->buf);
lbuf->buf = NULL;
}
/*
* Append strings to the buffer, expanding it as needed.
*/
void
#ifdef __STDC__
lbuf_append_quoted(struct lbuf *lbuf, const char *set, ...)
#else
lbuf_append_quoted(lbuf, set, va_alist)
struct lbuf *lbuf;
const char *set;
va_dcl
#endif
{
va_list ap;
int len = 0;
char *cp, *s;
#ifdef __STDC__
va_start(ap, set);
#else
va_start(ap);
#endif
while ((s = va_arg(ap, char *)) != NULL) {
len += strlen(s);
for (cp = s; (cp = strpbrk(cp, set)) != NULL; cp++)
len++;
}
va_end(ap);
/* Expand buffer as needed. */
if (lbuf->len + len >= lbuf->size) {
do {
lbuf->size += 256;
} while (lbuf->len + len >= lbuf->size);
lbuf->buf = erealloc(lbuf->buf, lbuf->size);
}
#ifdef __STDC__
va_start(ap, set);
#else
va_start(ap);
#endif
/* Append each string. */
while ((s = va_arg(ap, char *)) != NULL) {
while ((cp = strpbrk(s, set)) != NULL) {
len = (int)(cp - s);
memcpy(lbuf->buf + lbuf->len, s, len);
lbuf->len += len;
lbuf->buf[lbuf->len++] = '\\';
lbuf->buf[lbuf->len++] = *cp;
s = cp + 1;
}
if (*s != '\0') {
len = strlen(s);
memcpy(lbuf->buf + lbuf->len, s, len);
lbuf->len += len;
}
}
lbuf->buf[lbuf->len] = '\0';
va_end(ap);
}
/*
* Append strings to the buffer, expanding it as needed.
*/
void
#ifdef __STDC__
lbuf_append(struct lbuf *lbuf, ...)
#else
lbuf_append(lbuf, va_alist)
struct lbuf *lbuf;
va_dcl
#endif
{
va_list ap;
int len = 0;
char *s;
#ifdef __STDC__
va_start(ap, lbuf);
#else
va_start(ap);
#endif
while ((s = va_arg(ap, char *)) != NULL)
len += strlen(s);
va_end(ap);
/* Expand buffer as needed. */
if (lbuf->len + len >= lbuf->size) {
do {
lbuf->size += 256;
} while (lbuf->len + len >= lbuf->size);
lbuf->buf = erealloc(lbuf->buf, lbuf->size);
}
#ifdef __STDC__
va_start(ap, lbuf);
#else
va_start(ap);
#endif
/* Append each string. */
while ((s = va_arg(ap, char *)) != NULL) {
len = strlen(s);
memcpy(lbuf->buf + lbuf->len, s, len);
lbuf->len += len;
}
lbuf->buf[lbuf->len] = '\0';
va_end(ap);
}
/*
* Print the buffer with word wrap based on the tty width.
* The lbuf is reset on return.
*/
void
lbuf_print(lbuf)
struct lbuf *lbuf;
{
char *cp;
int i, have, contlen;
static int cols = -1;
if (cols == -1)
cols = get_ttycols();
contlen = lbuf->continuation ? 2 : 0;
/* For very small widths just give up... */
if (cols <= lbuf->indent + contlen + 20) {
puts(lbuf->buf);
goto done;
}
/*
* Print the buffer, splitting the line as needed on a word
* boundary.
*/
cp = lbuf->buf;
have = cols;
while (cp != NULL && *cp != '\0') {
char *ep;
int need = lbuf->len - (int)(cp - lbuf->buf);
ep = memrchr(cp, '\n', need > have ? have : need);
if (ep) {
need = ep - cp;
ep++; /* skip over newline */
} else if (need > have) {
have -= contlen; /* subtract for continuation char */
if ((ep = memrchr(cp, ' ', have)) == NULL)
ep = memchr(cp + have, ' ', need - have);
if (ep != NULL)
need = (int)(ep - cp);
}
if (cp != lbuf->buf) {
/* indent continued lines */
for (i = 0; i < lbuf->indent; i++)
putchar(' ');
}
fwrite(cp, need, 1, stdout);
cp = ep;
/*
* If there is more to print, reset have, incremement cp past
* the whitespace, and print a line continuaton char if needed.
*/
if (cp != NULL) {
have = cols - lbuf->indent;
do {
cp++;
} while (isspace((unsigned char)*cp));
if (lbuf->continuation) {
putchar(' ');
putchar(lbuf->continuation);
}
}
putchar('\n');
}
done:
lbuf->len = 0; /* reset the buffer for re-use. */
}

133
src/list.c Normal file
View File

@@ -0,0 +1,133 @@
/*
* Copyright (c) 2007-2008 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <config.h>
#include <sys/types.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#include "sudo.h"
struct list_proto {
struct list_proto *prev;
struct list_proto *next;
};
struct list_head_proto {
struct list_proto *first;
struct list_proto *last;
};
/*
* Pop the last element off the end of vh.
* Returns the popped element.
*/
void *
tq_pop(vh)
void *vh;
{
struct list_head_proto *h = (struct list_head_proto *)vh;
void *last = NULL;
if (!tq_empty(h)) {
last = (void *)h->last;
if (h->first == h->last) {
h->first = NULL;
h->last = NULL;
} else {
h->last = h->last->prev;
h->last->next = NULL;
}
}
return (last);
}
/*
* Convert from a semi-circle queue to normal doubly-linked list
* with a head node.
*/
void
list2tq(vh, vl)
void *vh;
void *vl;
{
struct list_head_proto *h = (struct list_head_proto *)vh;
struct list_proto *l = (struct list_proto *)vl;
if (l != NULL) {
#ifdef DEBUG
if (l->prev == NULL) {
warningx("list2tq called with non-semicircular list");
abort();
}
#endif
h->first = l;
h->last = l->prev; /* l->prev points to the last member of l */
l->prev = NULL; /* zero last ptr now that we have a head */
} else {
h->first = NULL;
h->last = NULL;
}
}
/*
* Append one queue (or single entry) to another using the
* circular properties of the prev pointer to simplify the logic.
*/
void
list_append(vl1, vl2)
void *vl1;
void *vl2;
{
struct list_proto *l1 = (struct list_proto *)vl1;
struct list_proto *l2 = (struct list_proto *)vl2;
void *tail = l2->prev;
l1->prev->next = l2;
l2->prev = l1->prev;
l1->prev = tail;
}
/*
* Append the list of entries to the head node and convert
* e from a semi-circle queue to normal doubly-linked list.
*/
void
tq_append(vh, vl)
void *vh;
void *vl;
{
struct list_head_proto *h = (struct list_head_proto *)vh;
struct list_proto *l = (struct list_proto *)vl;
void *tail = l->prev;
if (h->first == NULL)
h->first = l;
else
h->last->next = l;
l->prev = h->last;
h->last = tail;
}

200
src/pty.c Normal file
View File

@@ -0,0 +1,200 @@
/*
* Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#ifdef HAVE_SYS_STROPTS_H
#include <sys/stropts.h>
#endif /* HAVE_SYS_STROPTS_H */
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#ifdef HAVE_UTIL_H
# include <util.h>
#endif
#ifdef HAVE_PTY_H
# include <pty.h>
#endif
#include "sudo.h"
#if defined(HAVE_OPENPTY)
int
get_pty(master, slave, name, namesz)
int *master;
int *slave;
char *name;
size_t namesz;
{
struct group *gr;
gid_t ttygid = -1;
if ((gr = sudo_getgrnam("tty")) != NULL)
ttygid = gr->gr_gid;
if (openpty(master, slave, name, NULL, NULL) != 0)
return(0);
(void) chown(name, runas_pw->pw_uid, ttygid);
return(1);
}
#elif defined(HAVE__GETPTY)
int
get_pty(master, slave, name, namesz)
int *master;
int *slave;
char *name;
size_t namesz;
{
char *line;
/* IRIX-style dynamic ptys (may fork) */
line = _getpty(master, O_RDWR, IRUSR|S_IWUSR|S_IWGRP, 0);
if (line == NULL)
return (0);
*slave = open(line, O_RDWR|O_NOCTTY, 0);
if (*slave == -1) {
close(*master);
return(0);
}
(void) chown(line, runas_pw->pw_uid, -1);
strlcpy(name, line, namesz);
return(1);
}
#elif defined(HAVE_GRANTPT)
# ifndef HAVE_POSIX_OPENPT
static int
posix_openpt(oflag)
int oflag;
{
int fd;
# ifdef _AIX
fd = open("/dev/ptc", oflag);
# else
fd = open("/dev/ptmx", oflag);
# endif
return(fd);
}
# endif /* HAVE_POSIX_OPENPT */
int
get_pty(master, slave, name, namesz)
int *master;
int *slave;
char *name;
size_t namesz;
{
char *line;
*master = posix_openpt(O_RDWR|O_NOCTTY);
if (*master == -1)
return(0);
(void) grantpt(*master); /* may fork */
if (unlockpt(*master) != 0) {
close(*master);
return(0);
}
line = ptsname(*master);
if (line == NULL) {
close(*master);
return(0);
}
*slave = open(line, O_RDWR|O_NOCTTY, 0);
if (*slave == -1) {
close(*master);
return(0);
}
# ifdef I_PUSH
ioctl(*slave, I_PUSH, "ptem"); /* pseudo tty emulation module */
ioctl(*slave, I_PUSH, "ldterm"); /* line discipline module */
# endif
(void) chown(line, runas_pw->pw_uid, -1);
strlcpy(name, line, namesz);
return(1);
}
#else /* Old-style BSD ptys */
static char line[] = "/dev/ptyXX";
int
get_pty(master, slave, name, namesz)
int *master;
int *slave;
char *name;
size_t namesz;
{
char *bank, *cp;
struct group *gr;
gid_t ttygid = -1;
if ((gr = sudo_getgrnam("tty")) != NULL)
ttygid = gr->gr_gid;
for (bank = "pqrs"; *bank != '\0'; bank++) {
line[sizeof("/dev/ptyX") - 2] = *bank;
for (cp = "0123456789abcdef"; *cp != '\0'; cp++) {
line[sizeof("/dev/ptyXX") - 2] = *cp;
*master = open(line, O_RDWR|O_NOCTTY, 0);
if (*master == -1) {
if (errno == ENOENT)
return(0); /* out of ptys */
continue; /* already in use */
}
line[sizeof("/dev/p") - 2] = 't';
(void) chown(line, runas_pw->pw_uid, ttygid);
(void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP);
# ifdef HAVE_REVOKE
(void) revoke(line);
# endif
*slave = open(line, O_RDWR|O_NOCTTY, 0);
if (*slave != -1) {
strlcpy(name, line, namesz);
return(1); /* success */
}
(void) close(*master);
}
}
return(0);
}
#endif /* HAVE_OPENPTY */

1130
src/script.c Normal file

File diff suppressed because it is too large Load Diff

349
src/selinux.c Normal file
View File

@@ -0,0 +1,349 @@
/*
* Copyright (c) 2008 Dan Walsh <dwalsh@redhat.com>
*
* Borrowed heavily from newrole source code
* Authors:
* Anthony Colatrella
* Tim Fraser
* Steve Grubb <sgrubb@redhat.com>
* Darrel Goeddel <DGoeddel@trustedcs.com>
* Michael Thompson <mcthomps@us.ibm.com>
* Dan Walsh <dwalsh@redhat.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#ifdef WITH_AUDIT
#include <libaudit.h>
#endif
#include <selinux/flask.h> /* for SECCLASS_CHR_FILE */
#include <selinux/selinux.h> /* for is_selinux_enabled() */
#include <selinux/context.h> /* for context-mangling functions */
#include <selinux/get_default_type.h>
#include <selinux/get_context_list.h>
#include "sudo.h"
#include "pathnames.h"
static security_context_t old_context;
static security_context_t new_context;
static security_context_t tty_context;
static security_context_t new_tty_context;
static int enforcing;
/*
* This function attempts to revert the relabeling done to the tty.
* fd - referencing the opened ttyn
* ttyn - name of tty to restore
* tty_context - original context of the tty
* new_tty_context - context tty was relabeled to
*
* Returns zero on success, non-zero otherwise
*/
static int
restore_tty_label(int fd, const char *ttyn, security_context_t tty_context,
security_context_t new_tty_context)
{
int rc = 0;
security_context_t chk_tty_context = NULL;
if (!ttyn)
goto skip_relabel;
if (!new_tty_context)
goto skip_relabel;
/* Verify that the tty still has the context set by sudo. */
if ((rc = fgetfilecon(fd, &chk_tty_context)) < 0) {
warning("unable to fgetfilecon %s", ttyn);
goto skip_relabel;
}
if ((rc = strcmp(chk_tty_context, new_tty_context))) {
warningx("%s changed labels.", ttyn);
goto skip_relabel;
}
if ((rc = fsetfilecon(fd, tty_context)) < 0)
warning("unable to restore context for %s", ttyn);
skip_relabel:
freecon(chk_tty_context);
return(rc);
}
/*
* This function attempts to relabel the tty. If this function fails, then
* the contexts are free'd and -1 is returned. On success, 0 is returned
* and tty_context and new_tty_context are set.
*
* This function will not fail if it can not relabel the tty when selinux is
* in permissive mode.
*/
static int
relabel_tty(int ttyfd, security_context_t new_context,
security_context_t *tty_context, security_context_t *new_tty_context,
int enforcing)
{
security_context_t tty_con = NULL;
security_context_t new_tty_con = NULL;
if (fgetfilecon(ttyfd, &tty_con) < 0) {
warning("unable to get current tty context, not relabeling tty");
if (enforcing)
goto error;
}
if (tty_con && (security_compute_relabel(new_context, tty_con,
SECCLASS_CHR_FILE, &new_tty_con) < 0)) {
warning("unable to get new tty context, not relabeling tty");
if (enforcing)
goto error;
}
if (new_tty_con != NULL) {
if (fsetfilecon(ttyfd, new_tty_con) < 0) {
warning("unable to set new tty context");
if (enforcing)
goto error;
}
}
*tty_context = tty_con;
*new_tty_context = new_tty_con;
return(0);
error:
freecon(tty_con);
return(-1);
}
/*
* Returns a new security context based on the old context and the
* specified role and type.
*/
security_context_t
get_exec_context(security_context_t old_context, char *role, char *type)
{
security_context_t new_context = NULL;
context_t context = NULL;
char *typebuf = NULL;
/* We must have a role, the type is optional (we can use the default). */
if (!role) {
warningx("you must specify a role.");
return(NULL);
}
if (!type) {
if (get_default_type(role, &typebuf)) {
warningx("unable to get default type");
return(NULL);
}
type = typebuf;
}
/*
* Expand old_context into a context_t so that we extract and modify
* its components easily.
*/
context = context_new(old_context);
/*
* Replace the role and type in "context" with the role and
* type we will be running the command as.
*/
if (context_role_set(context, role)) {
warningx("failed to set new role %s", role);
goto error;
}
if (context_type_set(context, type)) {
warningx("failed to set new type %s", type);
goto error;
}
/*
* Convert "context" back into a string and verify it.
*/
new_context = estrdup(context_str(context));
if (security_check_context(new_context) < 0) {
warningx("%s is not a valid context", new_context);
goto error;
}
#ifdef DEBUG
warningx("Your new context is %s", new_context);
#endif
context_free(context);
return(new_context);
error:
free(typebuf);
context_free(context);
freecon(new_context);
return(NULL);
}
/*
* Set the tty context in preparation for fork/exec.
*/
void
selinux_prefork(char *role, char *type, int ttyfd)
{
/* Store the caller's SID in old_context. */
if (getprevcon(&old_context))
error(EXIT_FAILURE, "failed to get old_context");
enforcing = security_getenforce();
if (enforcing < 0)
error(EXIT_FAILURE, "unable to determine enforcing mode.");
#ifdef DEBUG
warningx("your old context was %s", old_context);
#endif
new_context = get_exec_context(old_context, role, type);
if (!new_context)
error(EXIT_FAILURE, "unable to get exec context");
ttyfd = relabel_tty(ttyfd, new_context, &tty_context,
&new_tty_context, enforcing);
if (ttyfd < 0)
error(EXIT_FAILURE, "unable to setup tty context for %s", new_context);
#ifdef DEBUG
warningx("your old tty context is %s", tty_context);
warningx("your new tty context is %s", new_tty_context);
#endif
}
void
selinux_execv(char *path, char **argv)
{
if (setexeccon(new_context)) {
warning("unable to set exec context to %s", new_context);
if (enforcing)
return;
}
if (setkeycreatecon(new_context)) {
warning("unable to set key creation context to %s", new_context);
if (enforcing)
return;
}
#ifdef WITH_AUDIT
if (send_audit_message(1, old_context, new_context, user_ttypath))
return;
#endif
/* We use the "spare" slot in argv to store sesh. */
--argv;
argv[0] = *argv[1] == '-' ? "-sesh" : "sesh";
argv[1] = path;
execv(_PATH_SUDO_SESH, argv);
warning("%s", path);
}
/*
* If the program is being run with a different security context we
* need to go through an intermediary process for the transition to
* be allowed by the policy. We use the "sesh" shell for this, which
* will simply execute the command pass to it on the command line.
*/
void
selinux_exec(char *role, char *type, char **argv)
{
pid_t childPid;
int ttyfd;
/* Must have a tty. */
if (user_ttypath == NULL || *user_ttypath == '\0')
error(EXIT_FAILURE, "unable to determine tty");
/* Re-open TTY descriptor */
ttyfd = open(user_ttypath, O_RDWR | O_NONBLOCK);
if (ttyfd == -1)
error(EXIT_FAILURE, "unable to open %s", user_ttypath);
(void)fcntl(ttyfd, F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
/*
* Get the old and new security and tty contexts, sets the new
* tty context on ttyfd.
*/
selinux_prefork(role, type, ttyfd);
childPid = fork();
if (childPid < 0) {
/* fork failed, no child to worry about */
warning("unable to fork");
if (restore_tty_label(ttyfd, user_ttypath, tty_context, new_tty_context))
warningx("unable to restore tty label");
exit(EXIT_FAILURE);
} else if (childPid) {
pid_t pid;
int status;
/* Parent, wait for child to finish. */
do {
pid = waitpid(childPid, &status, 0);
} while (pid == -1 && errno == EINTR);
if (pid == -1)
error(EXIT_FAILURE, "waitpid");
if (restore_tty_label(ttyfd, user_ttypath, tty_context, new_tty_context))
errorx(EXIT_FAILURE, "unable to restore tty label");
/* Preserve child exit status. */
if (WIFEXITED(status))
exit(WEXITSTATUS(status));
exit(EXIT_FAILURE);
}
/* Child */
/* Close the tty and reopen descriptors 0 through 2 */
if (close(ttyfd) || close(STDIN_FILENO) || close(STDOUT_FILENO) ||
close(STDERR_FILENO)) {
warning("could not close descriptors");
goto error;
}
ttyfd = open(user_ttypath, O_RDONLY | O_NONBLOCK);
if (ttyfd != STDIN_FILENO)
goto error;
fcntl(ttyfd, F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
ttyfd = open(user_ttypath, O_RDWR | O_NONBLOCK);
if (ttyfd != STDOUT_FILENO)
goto error;
fcntl(ttyfd, F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
ttyfd = dup(STDOUT_FILENO);
if (ttyfd != STDERR_FILENO)
goto error;
selinux_execv(safe_cmnd, argv);
error:
_exit(EXIT_FAILURE);
}

56
src/sesh.c Normal file
View File

@@ -0,0 +1,56 @@
/*
* Copyright (c) 2008 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <config.h>
#include <sys/types.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "compat.h"
int
main (int argc, char **argv)
{
char *cp, *cmnd;
if (argc < 2)
errx(EXIT_FAILURE, "requires at least one argument");
/* Shift argv and make a copy of the command to execute. */
argv++;
argc--;
cmnd = strdup(argv[0]);
if (cmnd == NULL)
err(EXIT_FAILURE, NULL);
/* If invoked as a login shell, modify argv[0] accordingly. */
if (argv[0][0] == '-') {
if ((cp = strrchr(argv[0], '/')) == NULL)
cp = argv[0];
*cp = '-';
}
execv(cmnd, argv);
warn("unable to execute %s", argv[0]);
_exit(EXIT_FAILURE);
}

382
src/sudo_edit.c Normal file
View File

@@ -0,0 +1,382 @@
/*
* Copyright (c) 2004-2008 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <ctype.h>
#include <pwd.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#if TIME_WITH_SYS_TIME
# include <time.h>
#endif
#ifndef HAVE_TIMESPEC
# include <emul/timespec.h>
#endif
#include "sudo.h"
extern sigaction_t saved_sa_int, saved_sa_quit, saved_sa_tstp;
extern char **environ;
static char *find_editor();
/*
* Wrapper to allow users to edit privileged files with their own uid.
*/
int
sudo_edit(argc, argv, envp)
int argc;
char **argv;
char **envp;
{
ssize_t nread, nwritten;
pid_t kidpid, pid;
const char *tmpdir;
char **nargv, **ap, *editor, *cp;
char buf[BUFSIZ];
int error, i, ac, ofd, tfd, nargc, rval, tmplen, wasblank;
struct stat sb;
struct timespec ts1, ts2;
struct tempfile {
char *tfile;
char *ofile;
struct timespec omtim;
off_t osize;
} *tf;
/*
* Find our temporary directory, one of /var/tmp, /usr/tmp, or /tmp
*/
if (stat(_PATH_VARTMP, &sb) == 0 && S_ISDIR(sb.st_mode))
tmpdir = _PATH_VARTMP;
#ifdef _PATH_USRTMP
else if (stat(_PATH_USRTMP, &sb) == 0 && S_ISDIR(sb.st_mode))
tmpdir = _PATH_USRTMP;
#endif
else
tmpdir = _PATH_TMP;
tmplen = strlen(tmpdir);
while (tmplen > 0 && tmpdir[tmplen - 1] == '/')
tmplen--;
/*
* Close password, shadow, and group files before we try to open
* user-specified files to prevent the opening of things like /dev/fd/4
*/
sudo_endpwent();
sudo_endgrent();
/*
* For each file specified by the user, make a temporary version
* and copy the contents of the original to it.
*/
tf = emalloc2(argc - 1, sizeof(*tf));
zero_bytes(tf, (argc - 1) * sizeof(*tf));
for (i = 0, ap = argv + 1; i < argc - 1 && *ap != NULL; i++, ap++) {
error = -1;
set_perms(PERM_RUNAS);
if ((ofd = open(*ap, O_RDONLY, 0644)) != -1 || errno == ENOENT) {
if (ofd == -1) {
zero_bytes(&sb, sizeof(sb)); /* new file */
error = 0;
} else {
#ifdef HAVE_FSTAT
error = fstat(ofd, &sb);
#else
error = stat(tf[i].ofile, &sb);
#endif
}
}
set_perms(PERM_ROOT);
if (error || (ofd != -1 && !S_ISREG(sb.st_mode))) {
if (error)
warning("%s", *ap);
else
warningx("%s: not a regular file", *ap);
if (ofd != -1)
close(ofd);
argc--;
i--;
continue;
}
tf[i].ofile = *ap;
tf[i].omtim.tv_sec = mtim_getsec(sb);
tf[i].omtim.tv_nsec = mtim_getnsec(sb);
tf[i].osize = sb.st_size;
if ((cp = strrchr(tf[i].ofile, '/')) != NULL)
cp++;
else
cp = tf[i].ofile;
easprintf(&tf[i].tfile, "%.*s/%s.XXXXXXXX", tmplen, tmpdir, cp);
set_perms(PERM_USER);
tfd = mkstemp(tf[i].tfile);
set_perms(PERM_ROOT);
if (tfd == -1) {
warning("mkstemp");
goto cleanup;
}
if (ofd != -1) {
while ((nread = read(ofd, buf, sizeof(buf))) != 0) {
if ((nwritten = write(tfd, buf, nread)) != nread) {
if (nwritten == -1)
warning("%s", tf[i].tfile);
else
warningx("%s: short write", tf[i].tfile);
goto cleanup;
}
}
close(ofd);
}
/*
* We always update the stashed mtime because the time
* resolution of the filesystem the temporary file is on may
* not match that of the filesystem where the file to be edited
* resides. It is OK if touch() fails since we only use the info
* to determine whether or not a file has been modified.
*/
(void) touch(tfd, NULL, &tf[i].omtim);
#ifdef HAVE_FSTAT
error = fstat(tfd, &sb);
#else
error = stat(tf[i].tfile, &sb);
#endif
if (!error) {
tf[i].omtim.tv_sec = mtim_getsec(sb);
tf[i].omtim.tv_nsec = mtim_getnsec(sb);
}
close(tfd);
}
if (argc == 1)
return(1); /* no files readable, you lose */
environ = envp;
editor = find_editor();
/*
* Allocate space for the new argument vector and fill it in.
* The EDITOR and VISUAL environment variables may contain command
* line args so look for those and alloc space for them too.
*/
nargc = argc;
for (wasblank = FALSE, cp = editor; *cp != '\0'; cp++) {
if (isblank((unsigned char) *cp))
wasblank = TRUE;
else if (wasblank) {
wasblank = FALSE;
nargc++;
}
}
nargv = (char **) emalloc2(nargc + 1, sizeof(char *));
ac = 0;
for ((cp = strtok(editor, " \t")); cp != NULL; (cp = strtok(NULL, " \t")))
nargv[ac++] = cp;
for (i = 0; i < argc - 1 && ac < nargc; )
nargv[ac++] = tf[i++].tfile;
nargv[ac] = NULL;
/* Allow the editor to be suspended. */
(void) sigaction(SIGTSTP, &saved_sa_tstp, NULL);
/*
* Fork and exec the editor with the invoking user's creds,
* keeping track of the time spent in the editor.
*/
gettime(&ts1);
kidpid = fork();
if (kidpid == -1) {
warning("fork");
goto cleanup;
} else if (kidpid == 0) {
/* child */
(void) sigaction(SIGINT, &saved_sa_int, NULL);
(void) sigaction(SIGQUIT, &saved_sa_quit, NULL);
set_perms(PERM_FULL_USER);
closefrom(def_closefrom);
execvp(nargv[0], nargv);
warning("unable to execute %s", nargv[0]);
_exit(127);
}
/*
* Wait for status from the child. Most modern kernels
* will not let an unprivileged child process send a
* signal to its privileged parent so we have to request
* status when the child is stopped and then send the
* same signal to our own pid.
*/
do {
#ifdef sudo_waitpid
pid = sudo_waitpid(kidpid, &i, WUNTRACED);
#else
pid = wait(&i);
#endif
if (pid == kidpid) {
if (WIFSTOPPED(i))
kill(getpid(), WSTOPSIG(i));
else
break;
}
} while (pid != -1 || errno == EINTR);
gettime(&ts2);
if (pid == -1 || !WIFEXITED(i))
rval = 1;
else
rval = WEXITSTATUS(i);
/* Copy contents of temp files to real ones */
for (i = 0; i < argc - 1; i++) {
error = -1;
set_perms(PERM_USER);
if ((tfd = open(tf[i].tfile, O_RDONLY, 0644)) != -1) {
#ifdef HAVE_FSTAT
error = fstat(tfd, &sb);
#else
error = stat(tf[i].tfile, &sb);
#endif
}
set_perms(PERM_ROOT);
if (error || !S_ISREG(sb.st_mode)) {
if (error)
warning("%s", tf[i].tfile);
else
warningx("%s: not a regular file", tf[i].tfile);
warningx("%s left unmodified", tf[i].ofile);
if (tfd != -1)
close(tfd);
continue;
}
if (tf[i].osize == sb.st_size && tf[i].omtim.tv_sec == mtim_getsec(sb)
&& tf[i].omtim.tv_nsec == mtim_getnsec(sb)) {
/*
* If mtime and size match but the user spent no measurable
* time in the editor we can't tell if the file was changed.
*/
#ifdef HAVE_TIMESPECSUB2
timespecsub(&ts1, &ts2);
#else
timespecsub(&ts1, &ts2, &ts2);
#endif
if (timespecisset(&ts2)) {
warningx("%s unchanged", tf[i].ofile);
unlink(tf[i].tfile);
close(tfd);
continue;
}
}
set_perms(PERM_RUNAS);
ofd = open(tf[i].ofile, O_WRONLY|O_TRUNC|O_CREAT, 0644);
set_perms(PERM_ROOT);
if (ofd == -1) {
warning("unable to write to %s", tf[i].ofile);
warningx("contents of edit session left in %s", tf[i].tfile);
close(tfd);
continue;
}
while ((nread = read(tfd, buf, sizeof(buf))) > 0) {
if ((nwritten = write(ofd, buf, nread)) != nread) {
if (nwritten == -1)
warning("%s", tf[i].ofile);
else
warningx("%s: short write", tf[i].ofile);
break;
}
}
if (nread == 0) {
/* success, got EOF */
unlink(tf[i].tfile);
} else if (nread < 0) {
warning("unable to read temporary file");
warningx("contents of edit session left in %s", tf[i].tfile);
} else {
warning("unable to write to %s", tf[i].ofile);
warningx("contents of edit session left in %s", tf[i].tfile);
}
close(ofd);
}
return(rval);
cleanup:
/* Clean up temp files and return. */
for (i = 0; i < argc - 1; i++) {
if (tf[i].tfile != NULL)
unlink(tf[i].tfile);
}
return(1);
}
/*
* Determine which editor to use. We don't bother restricting this
* based on def_env_editor or def_editor since the editor runs with
* the uid of the invoking user, not the runas (privileged) user.
*/
static char *
find_editor()
{
char *cp, *editor = NULL, **ev, *ev0[4];
ev0[0] = "SUDO_EDITOR";
ev0[1] = "VISUAL";
ev0[2] = "EDITOR";
ev0[3] = NULL;
for (ev = ev0; *ev != NULL; ev++) {
if ((editor = getenv(*ev)) != NULL && *editor != '\0') {
if ((cp = strrchr(editor, '/')) != NULL)
cp++;
else
cp = editor;
/* Ignore "sudoedit" and "sudo" to avoid an endless loop. */
if (strncmp(cp, "sudo", 4) != 0 ||
(cp[4] != ' ' && cp[4] != '\0' && strcmp(cp + 4, "edit") != 0)) {
editor = estrdup(editor);
break;
}
}
editor = NULL;
}
if (editor == NULL) {
editor = estrdup(def_editor);
if ((cp = strchr(editor, ':')) != NULL)
*cp = '\0'; /* def_editor could be a path */
}
return(editor);
}

111
src/sudo_noexec.c Normal file
View File

@@ -0,0 +1,111 @@
/*
* Copyright (c) 2004-2005 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <errno.h>
#ifndef HAVE_TIMESPEC
# include <time.h>
#endif
#ifdef __STDC__
# include <stdarg.h>
#else
# include <varargs.h>
#endif
#include <compat.h>
/*
* Dummy versions of the execve() family of syscalls. We don't need
* to stub out all of them, just the ones that correspond to actual
* system calls (which varies by OS). Note that it is still possible
* to access the real syscalls via the syscall() interface but very
* few programs actually do that.
*/
#ifndef errno
extern int errno;
#endif
#define DUMMY_BODY \
{ \
errno = EACCES; \
return(-1); \
}
#ifdef __STDC__
#define DUMMY2(fn, t1, t2) \
int \
fn(t1 a1, t2 a2) \
DUMMY_BODY
#define DUMMY3(fn, t1, t2, t3) \
int \
fn(t1 a1, t2 a2, t3 a3) \
DUMMY_BODY
#define DUMMY_VA(fn, t1, t2) \
int \
fn(t1 a1, t2 a2, ...) \
DUMMY_BODY
#else /* !__STDC__ */
#define DUMMY2(fn, t1, t2) \
int \
fn(a1, a2) \
t1 a1; t2 a2; \
DUMMY_BODY
#define DUMMY3(fn, t1, t2, t3) \
int \
fn(a1, a2, a3) \
t1 a1; t2 a2; t3 a3; \
DUMMY_BODY
#define DUMMY_VA(fn, t1, t2) \
int \
fn(a1, a2, va_alist) \
t1 a1; t2 a2; va_dcl \
DUMMY_BODY
#endif /* !__STDC__ */
DUMMY_VA(execl, const char *, const char *)
DUMMY_VA(_execl, const char *, const char *)
DUMMY_VA(__execl, const char *, const char *)
DUMMY_VA(execle, const char *, const char *)
DUMMY_VA(_execle, const char *, const char *)
DUMMY_VA(__execle, const char *, const char *)
DUMMY_VA(execlp, const char *, const char *)
DUMMY_VA(_execlp, const char *, const char *)
DUMMY_VA(__execlp, const char *, const char *)
DUMMY2(execv, const char *, char * const *)
DUMMY2(_execv, const char *, char * const *)
DUMMY2(__execv, const char *, char * const *)
DUMMY2(execvp, const char *, char * const *)
DUMMY2(_execvp, const char *, char * const *)
DUMMY2(__execvp, const char *, char * const *)
DUMMY3(execvP, const char *, const char *, char * const *)
DUMMY3(_execvP, const char *, const char *, char * const *)
DUMMY3(__execvP, const char *, const char *, char * const *)
DUMMY3(execve, const char *, char * const *, char * const *)
DUMMY3(_execve, const char *, char * const *, char * const *)
DUMMY3(__execve, const char *, char * const *, char * const *)
DUMMY3(fexecve, int , char * const *, char * const *)
DUMMY3(_fexecve, int , char * const *, char * const *)
DUMMY3(__fexecve, int , char * const *, char * const *)

282
src/term.c Normal file
View File

@@ -0,0 +1,282 @@
/*
* Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_TERMIOS_H
# include <termios.h>
#else
# ifdef HAVE_TERMIO_H
# include <termio.h>
# else
# include <sgtty.h>
# include <sys/ioctl.h>
# endif /* HAVE_TERMIO_H */
#endif /* HAVE_TERMIOS_H */
#include "sudo.h"
#ifndef TCSASOFT
# define TCSASOFT 0
#endif
#ifndef ECHONL
# define ECHONL 0
#endif
#ifndef IEXTEN
# define IEXTEN 0
#endif
#ifndef IUCLC
# define IUCLC 0
#endif
#ifndef _POSIX_VDISABLE
# ifdef VDISABLE
# define _POSIX_VDISABLE VDISABLE
# else
# define _POSIX_VDISABLE 0
# endif
#endif
/*
* Compat macros for non-termios systems.
*/
#ifndef HAVE_TERMIOS_H
# ifdef HAVE_TERMIO_H
# undef termios
# define termios termio
# define tcgetattr(f, t) ioctl(f, TCGETA, t)
# define tcsetattr(f, a, t) ioctl(f, a, t)
# undef TCSAFLUSH
# define TCSAFLUSH TCSETAF
# undef TCSADRAIN
# define TCSADRAIN TCSETAW
# else /* SGTTY */
# undef termios
# define termios sgttyb
# define c_lflag sg_flags
# define tcgetattr(f, t) ioctl(f, TIOCGETP, t)
# define tcsetattr(f, a, t) ioctl(f, a, t)
# undef TCSAFLUSH
# define TCSAFLUSH TIOCSETP
# undef TCSADRAIN
# define TCSADRAIN TIOCSETN
# endif /* HAVE_TERMIO_H */
#endif /* HAVE_TERMIOS_H */
typedef struct termios sudo_term_t;
static sudo_term_t term, oterm;
static int changed;
int term_erase;
int term_kill;
int
term_restore(fd, flush)
int fd;
int flush;
{
if (changed) {
int flags = TCSASOFT;
flags |= flush ? TCSAFLUSH : TCSADRAIN;
if (tcsetattr(fd, flags, &oterm) != 0)
return(0);
changed = 0;
}
return(1);
}
int
term_noecho(fd)
int fd;
{
if (!changed && tcgetattr(fd, &oterm) != 0)
return(0);
(void) memcpy(&term, &oterm, sizeof(term));
CLR(term.c_lflag, ECHO|ECHONL);
#ifdef VSTATUS
term.c_cc[VSTATUS] = _POSIX_VDISABLE;
#endif
if (tcsetattr(fd, TCSADRAIN|TCSASOFT, &term) == 0) {
changed = 1;
return(1);
}
return(0);
}
#if defined(HAVE_TERMIOS_H) || defined(HAVE_TERMIO_H)
int
term_raw(fd, opost, isig)
int fd;
int opost;
int isig;
{
struct termios term;
if (!changed && tcgetattr(fd, &oterm) != 0)
return(0);
(void) memcpy(&term, &oterm, sizeof(term));
/* Set terminal to raw mode */
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0;
CLR(term.c_lflag, ECHO | ECHONL | ICANON | ISIG | IEXTEN);
if (isig)
SET(term.c_lflag, ISIG);
CLR(term.c_iflag, ICRNL | IGNCR | INLCR | IUCLC | IXON);
/* Only retain output post-processing opost flag set. */
if (!opost)
CLR(term.c_oflag, OPOST);
if (tcsetattr(fd, TCSADRAIN|TCSASOFT, &term) == 0) {
changed = 1;
return(1);
}
return(0);
}
int
term_cbreak(fd)
int fd;
{
if (!changed && tcgetattr(fd, &oterm) != 0)
return(0);
(void) memcpy(&term, &oterm, sizeof(term));
/* Set terminal to half-cooked mode */
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0;
CLR(term.c_lflag, ECHO | ECHONL | ICANON | IEXTEN);
SET(term.c_lflag, ISIG);
#ifdef VSTATUS
term.c_cc[VSTATUS] = _POSIX_VDISABLE;
#endif
if (tcsetattr(fd, TCSADRAIN|TCSASOFT, &term) == 0) {
term_erase = term.c_cc[VERASE];
term_kill = term.c_cc[VKILL];
changed = 1;
return(1);
}
return(0);
}
int
term_copy(src, dst, opost)
int src;
int dst;
int opost;
{
struct termios tt;
if (tcgetattr(src, &tt) != 0)
return(0);
/* Do not do post-processing unless opost set. */
if (!opost)
CLR(tt.c_oflag, OPOST);
/* XXX - add TCSANOW compat define */
if (tcsetattr(dst, TCSANOW|TCSASOFT, &tt) != 0)
return(0);
return(1);
}
#else /* SGTTY */
int
term_raw(fd, onlcr)
int fd;
int onlcr;
{
if (!changed && ioctl(fd, TIOCGETP, &oterm) != 0)
return(0);
(void) memcpy(&term, &oterm, sizeof(term));
/* Set terminal to raw mode */
CLR(term.c_lflag, ECHO);
SET(term.sg_flags, RAW);
/* Retain NL to NLCR conversion if onlcr flag set. */
if (onlcr)
SET(term.sg_flags, CRMOD);
if (ioctl(fd, TIOCSETP, &term) == 0) {
changed = 1;
return(1);
}
return(0);
}
int
term_cbreak(fd)
int fd;
{
if (!changed && ioctl(fd, TIOCGETP, &oterm) != 0)
return(0);
(void) memcpy(&term, &oterm, sizeof(term));
/* Set terminal to half-cooked mode */
CLR(term.c_lflag, ECHO);
SET(term.sg_flags, CBREAK);
if (ioctl(fd, TIOCSETP, &term) == 0) {
term_erase = term.sg_erase;
term_kill = term.sg_kill;
changed = 1;
return(1);
}
return(0);
}
int
term_copy(src, dst, onlcr)
int src;
int dst;
int onlcr;
{
struct sgttyb b;
struct tchars tc;
struct ltchars lc;
int l, lb;
if (ioctl(src, TIOCGETP, &b) != 0 || ioctl(src, TIOCGETC, &tc) != 0 ||
ioctl(src, TIOCGETD, &l) != 0 || ioctl(src, TIOCGLTC, &lc) != 0 ||
ioctl(src, TIOCLGET, &lb)) {
return(0);
}
/* Do not convert line endings from NL to NLCR. */
if (!onlcr)
CLR(b.sg_flags, CRMOD);
if (ioctl(dst, TIOCSETP, &b) != 0 || ioctl(dst, TIOCSETC, &tc) != 0 ||
ioctl(dst, TIOCSLTC, &lc) != 0 || ioctl(dst, TIOCLSET, &lb) != 0 ||
ioctl(dst, TIOCSETD, &l) != 0) {
return(0);
}
return(1);
}
#endif

281
src/tgetpass.c Normal file
View File

@@ -0,0 +1,281 @@
/*
* Copyright (c) 1996, 1998-2005, 2007-2009
* Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#ifdef __TANDEM
# include <floss.h>
#endif
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif /* HAVE_STRING_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include "sudo.h"
static volatile sig_atomic_t signo;
static void handler __P((int));
static char *getln __P((int, char *, size_t, int));
static char *sudo_askpass __P((const char *));
/*
* Like getpass(3) but with timeout and echo flags.
*/
char *
tgetpass(prompt, timeout, flags)
const char *prompt;
int timeout;
int flags;
{
sigaction_t sa, savealrm, saveint, savehup, savequit, saveterm;
sigaction_t savetstp, savettin, savettou;
char *pass;
static char buf[SUDO_PASS_MAX + 1];
int input, output, save_errno, neednl;;
(void) fflush(stdout);
/* If using a helper program to get the password, run it instead. */
if (ISSET(flags, TGP_ASKPASS) && user_askpass)
return(sudo_askpass(prompt));
restart:
signo = 0;
pass = NULL;
save_errno = 0;
/* Open /dev/tty for reading/writing if possible else use stdin/stderr. */
if (ISSET(flags, TGP_STDIN) ||
(input = output = open(_PATH_TTY, O_RDWR|O_NOCTTY)) == -1) {
input = STDIN_FILENO;
output = STDERR_FILENO;
}
/*
* Catch signals that would otherwise cause the user to end
* up with echo turned off in the shell.
*/
zero_bytes(&sa, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_INTERRUPT; /* don't restart system calls */
sa.sa_handler = handler;
(void) sigaction(SIGALRM, &sa, &savealrm);
(void) sigaction(SIGINT, &sa, &saveint);
(void) sigaction(SIGHUP, &sa, &savehup);
(void) sigaction(SIGQUIT, &sa, &savequit);
(void) sigaction(SIGTERM, &sa, &saveterm);
(void) sigaction(SIGTSTP, &sa, &savetstp);
(void) sigaction(SIGTTIN, &sa, &savettin);
(void) sigaction(SIGTTOU, &sa, &savettou);
if (def_pwfeedback)
neednl = term_cbreak(input);
else
neednl = term_noecho(input);
/* No output if we are already backgrounded. */
if (signo != SIGTTOU && signo != SIGTTIN) {
if (prompt)
(void) write(output, prompt, strlen(prompt));
if (timeout > 0)
alarm(timeout);
pass = getln(input, buf, sizeof(buf), def_pwfeedback);
alarm(0);
save_errno = errno;
if (neednl)
(void) write(output, "\n", 1);
}
/* Restore old tty settings and signals. */
term_restore(input, 1);
(void) sigaction(SIGALRM, &savealrm, NULL);
(void) sigaction(SIGINT, &saveint, NULL);
(void) sigaction(SIGHUP, &savehup, NULL);
(void) sigaction(SIGQUIT, &savequit, NULL);
(void) sigaction(SIGTERM, &saveterm, NULL);
(void) sigaction(SIGTSTP, &savetstp, NULL);
(void) sigaction(SIGTTIN, &savettin, NULL);
(void) sigaction(SIGTTOU, &savettou, NULL);
if (input != STDIN_FILENO)
(void) close(input);
/*
* If we were interrupted by a signal, resend it to ourselves
* now that we have restored the signal handlers.
*/
if (signo) {
kill(getpid(), signo);
switch (signo) {
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
goto restart;
}
}
if (save_errno)
errno = save_errno;
return(pass);
}
/*
* Fork a child and exec sudo-askpass to get the password from the user.
*/
static char *
sudo_askpass(prompt)
const char *prompt;
{
static char buf[SUDO_PASS_MAX + 1], *pass;
sigaction_t sa, saved_sa_pipe;
int pfd[2];
pid_t pid;
if (pipe(pfd) == -1)
error(1, "unable to create pipe");
if ((pid = fork()) == -1)
error(1, "unable to fork");
if (pid == 0) {
/* child, point stdout to output side of the pipe and exec askpass */
(void) dup2(pfd[1], STDOUT_FILENO);
set_perms(PERM_FULL_USER);
closefrom(STDERR_FILENO + 1);
execl(user_askpass, user_askpass, prompt, (char *)NULL);
warning("unable to run %s", user_askpass);
_exit(255);
}
/* Ignore SIGPIPE in case child exits prematurely */
zero_bytes(&sa, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = SIG_IGN;
(void) sigaction(SIGPIPE, &sa, &saved_sa_pipe);
/* Get response from child (askpass) and restore SIGPIPE handler */
(void) close(pfd[1]);
pass = getln(pfd[0], buf, sizeof(buf), 0);
(void) close(pfd[0]);
(void) sigaction(SIGPIPE, &saved_sa_pipe, NULL);
return(pass);
}
extern int term_erase, term_kill;
static char *
getln(fd, buf, bufsiz, feedback)
int fd;
char *buf;
size_t bufsiz;
int feedback;
{
size_t left = bufsiz;
ssize_t nr = -1;
char *cp = buf;
char c = '\0';
if (left == 0) {
errno = EINVAL;
return(NULL); /* sanity */
}
while (--left) {
nr = read(fd, &c, 1);
if (nr != 1 || c == '\n' || c == '\r')
break;
if (feedback) {
if (c == term_kill) {
while (cp > buf) {
(void) write(fd, "\b \b", 3);
--cp;
}
left = bufsiz;
continue;
} else if (c == term_erase) {
if (cp > buf) {
(void) write(fd, "\b \b", 3);
--cp;
left++;
}
continue;
}
(void) write(fd, "*", 1);
}
*cp++ = c;
}
*cp = '\0';
if (feedback) {
/* erase stars */
while (cp > buf) {
(void) write(fd, "\b \b", 3);
--cp;
}
}
return(nr == 1 ? buf : NULL);
}
static void
handler(s)
int s;
{
if (s != SIGALRM)
signo = s;
}
int
tty_present()
{
int fd;
if ((fd = open(_PATH_TTY, O_RDWR|O_NOCTTY)) != -1)
close(fd);
return(fd != -1);
}

36
src/zero_bytes.c Normal file
View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2003-2005, 2007 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <config.h>
#include <compat.h>
/*
* Like bzero(3) but with a volatile pointer. The hope is that
* the compiler will not be able to optimize away this function.
*/
void
zero_bytes(v, n)
volatile void *v;
size_t n;
{
volatile char *p, *ep;
for (p = v, ep = p + n; p < ep; p++)
*p = 0;
return;
}