version 1.11, 2003/04/19 21:57:17 |
version 1.12, 2004/09/28 15:10:50 |
|
|
/* |
/* |
* Copyright (c) 1993-1996,1998-2003 Todd C. Miller <Todd.Miller@courtesan.com> |
* Copyright (c) 1993-1996,1998-2004 Todd C. Miller <Todd.Miller@courtesan.com> |
* All rights reserved. |
|
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Permission to use, copy, modify, and distribute this software for any |
* modification, are permitted provided that the following conditions |
* purpose with or without fee is hereby granted, provided that the above |
* are met: |
* copyright notice and this permission notice appear in all copies. |
* |
* |
* 1. Redistributions of source code must retain the above copyright |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
* notice, this list of conditions and the following disclaimer. |
* 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. |
* |
* |
* 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. |
|
* |
|
* 3. The name of the author may not be used to endorse or promote products |
|
* derived from this software without specific prior written permission. |
|
* |
|
* 4. Products derived from this software may not be called "Sudo" nor |
|
* may "Sudo" appear in their names without specific prior written |
|
* permission from the author. |
|
* |
|
* THIS SOFTWARE IS PROVIDED ``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 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. |
|
* |
|
* Sponsored in part by the Defense Advanced Research Projects |
* Sponsored in part by the Defense Advanced Research Projects |
* Agency (DARPA) and Air Force Research Laboratory, Air Force |
* Agency (DARPA) and Air Force Research Laboratory, Air Force |
* Materiel Command, USAF, under agreement number F39502-99-1-0512. |
* Materiel Command, USAF, under agreement number F39502-99-1-0512. |
|
|
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/param.h> |
#include <sys/param.h> |
#include <sys/stat.h> |
#include <sys/stat.h> |
#include <sys/file.h> |
#ifndef __TANDEM |
|
# include <sys/file.h> |
|
#endif |
#include <stdio.h> |
#include <stdio.h> |
#ifdef STDC_HEADERS |
#ifdef STDC_HEADERS |
# include <stdlib.h> |
# include <stdlib.h> |
|
|
#include "sudo.h" |
#include "sudo.h" |
|
|
#ifndef lint |
#ifndef lint |
static const char rcsid[] = "$Sudo: check.c,v 1.213 2003/04/16 00:42:09 millert Exp $"; |
static const char rcsid[] = "$Sudo: check.c,v 1.226 2004/09/08 15:48:23 millert Exp $"; |
#endif /* lint */ |
#endif /* lint */ |
|
|
/* Status codes for timestamp_status() */ |
/* Status codes for timestamp_status() */ |
|
|
static void build_timestamp __P((char **, char **)); |
static void build_timestamp __P((char **, char **)); |
static int timestamp_status __P((char *, char *, char *, int)); |
static int timestamp_status __P((char *, char *, char *, int)); |
static char *expand_prompt __P((char *, char *, char *)); |
static char *expand_prompt __P((char *, char *, char *)); |
static void lecture __P((void)); |
static void lecture __P((int)); |
static void update_timestamp __P((char *, char *)); |
static void update_timestamp __P((char *, char *)); |
|
|
/* |
/* |
* This function only returns if the user can successfully |
* This function only returns if the user can successfully |
* verify who he/she is. |
* verify who he/she is. |
*/ |
*/ |
void |
void |
check_user() |
check_user(override) |
|
int override; |
{ |
{ |
char *timestampdir = NULL; |
char *timestampdir = NULL; |
char *timestampfile = NULL; |
char *timestampfile = NULL; |
char *prompt; |
char *prompt; |
int status; |
int status; |
|
|
if (user_uid == 0 || user_is_exempt()) |
if (user_uid == 0 || user_uid == runas_pw->pw_uid || user_is_exempt()) |
return; |
return; |
|
|
build_timestamp(×tampdir, ×tampfile); |
build_timestamp(×tampdir, ×tampfile); |
status = timestamp_status(timestampdir, timestampfile, user_name, TRUE); |
status = timestamp_status(timestampdir, timestampfile, user_name, TRUE); |
if (status != TS_CURRENT) { |
if (override || status != TS_CURRENT) { |
if (status == TS_MISSING || status == TS_ERROR) |
lecture(status); |
lecture(); /* first time through they get a lecture */ |
|
|
|
/* Expand any escapes in the prompt. */ |
/* Expand any escapes in the prompt. */ |
prompt = expand_prompt(user_prompt ? user_prompt : def_str(I_PASSPROMPT), |
prompt = expand_prompt(user_prompt ? user_prompt : def_passprompt, |
user_name, user_shost); |
user_name, user_shost); |
|
|
verify_user(auth_pw, prompt); |
verify_user(auth_pw, prompt); |
|
|
* TODO: allow the user to specify a file name instead. |
* TODO: allow the user to specify a file name instead. |
*/ |
*/ |
static void |
static void |
lecture() |
lecture(status) |
|
int status; |
{ |
{ |
|
FILE *fp; |
|
char buf[BUFSIZ]; |
|
ssize_t nread; |
|
|
if (def_flag(I_LECTURE)) { |
if (def_lecture == never || |
|
(def_lecture == once && status != TS_MISSING && status != TS_ERROR)) |
|
return; |
|
|
|
if (def_lecture_file && (fp = fopen(def_lecture_file, "r")) != NULL) { |
|
while ((nread = fread(buf, sizeof(char), sizeof(buf), fp)) != 0) |
|
fwrite(buf, nread, 1, stderr); |
|
} else { |
(void) fputs("\n\ |
(void) fputs("\n\ |
We trust you have received the usual lecture from the local System\n\ |
We trust you have received the usual lecture from the local System\n\ |
Administrator. It usually boils down to these two things:\n\ |
Administrator. It usually boils down to these three things:\n\ |
\n\ |
\n\ |
#1) Respect the privacy of others.\n\ |
#1) Respect the privacy of others.\n\ |
#2) Think before you type.\n\n", |
#2) Think before you type.\n\ |
stderr); |
#3) With great power comes great responsibility.\n\n", |
|
stderr); |
} |
} |
} |
} |
|
|
|
|
char *timestampdir; |
char *timestampdir; |
char *timestampfile; |
char *timestampfile; |
{ |
{ |
|
|
if (timestamp_uid != 0) |
if (timestamp_uid != 0) |
set_perms(PERM_TIMESTAMP); |
set_perms(PERM_TIMESTAMP); |
if (touch(timestampfile ? timestampfile : timestampdir, time(NULL)) == -1) { |
if (touch(-1, timestampfile ? timestampfile : timestampdir, NULL) == -1) { |
if (timestampfile) { |
if (timestampfile) { |
int fd = open(timestampfile, O_WRONLY|O_CREAT|O_TRUNC, 0600); |
int fd = open(timestampfile, O_WRONLY|O_CREAT|O_TRUNC, 0600); |
|
|
|
|
struct group *grp; |
struct group *grp; |
char **gr_mem; |
char **gr_mem; |
|
|
if (!def_str(I_EXEMPT_GROUP)) |
if (!def_exempt_group) |
return(FALSE); |
return(FALSE); |
|
|
if (!(grp = getgrnam(def_str(I_EXEMPT_GROUP)))) |
if (!(grp = getgrnam(def_exempt_group))) |
return(FALSE); |
return(FALSE); |
|
|
if (user_gid == grp->gr_gid) |
if (user_gid == grp->gr_gid) |
|
|
char *dirparent; |
char *dirparent; |
int len; |
int len; |
|
|
dirparent = def_str(I_TIMESTAMPDIR); |
dirparent = def_timestampdir; |
len = easprintf(timestampdir, "%s/%s", dirparent, user_name); |
len = easprintf(timestampdir, "%s/%s", dirparent, user_name); |
if (len >= MAXPATHLEN) |
if (len >= PATH_MAX) |
log_error(0, "timestamp path too long: %s", timestampdir); |
log_error(0, "timestamp path too long: %s", timestampdir); |
|
|
/* |
/* |
* Timestamp file may be a file in the directory or NUL to use |
* Timestamp file may be a file in the directory or NUL to use |
* the directory as the timestamp. |
* the directory as the timestamp. |
*/ |
*/ |
if (def_flag(I_TTY_TICKETS)) { |
if (def_tty_tickets) { |
char *p; |
char *p; |
|
|
if ((p = strrchr(user_tty, '/'))) |
if ((p = strrchr(user_tty, '/'))) |
p++; |
p++; |
else |
else |
p = user_tty; |
p = user_tty; |
if (def_flag(I_TARGETPW)) |
if (def_targetpw) |
len = easprintf(timestampfile, "%s/%s/%s:%s", dirparent, user_name, |
len = easprintf(timestampfile, "%s/%s/%s:%s", dirparent, user_name, |
p, *user_runas); |
p, *user_runas); |
else |
else |
len = easprintf(timestampfile, "%s/%s/%s", dirparent, user_name, p); |
len = easprintf(timestampfile, "%s/%s/%s", dirparent, user_name, p); |
if (len >= MAXPATHLEN) |
if (len >= PATH_MAX) |
log_error(0, "timestamp path too long: %s", timestampfile); |
log_error(0, "timestamp path too long: %s", timestampfile); |
} else if (def_flag(I_TARGETPW)) { |
} else if (def_targetpw) { |
len = easprintf(timestampfile, "%s/%s/%s", dirparent, user_name, |
len = easprintf(timestampfile, "%s/%s/%s", dirparent, user_name, |
*user_runas); |
*user_runas); |
if (len >= MAXPATHLEN) |
if (len >= PATH_MAX) |
log_error(0, "timestamp path too long: %s", timestampfile); |
log_error(0, "timestamp path too long: %s", timestampfile); |
} else |
} else |
*timestampfile = NULL; |
*timestampfile = NULL; |
|
|
{ |
{ |
struct stat sb; |
struct stat sb; |
time_t now; |
time_t now; |
char *dirparent = def_str(I_TIMESTAMPDIR); |
char *dirparent = def_timestampdir; |
int status = TS_ERROR; /* assume the worst */ |
int status = TS_ERROR; /* assume the worst */ |
|
|
if (timestamp_uid != 0) |
if (timestamp_uid != 0) |
|
|
*/ |
*/ |
if (status == TS_OLD) { |
if (status == TS_OLD) { |
/* Negative timeouts only expire manually (sudo -k). */ |
/* Negative timeouts only expire manually (sudo -k). */ |
if (def_ival(I_TIMESTAMP_TIMEOUT) < 0 && sb.st_mtime != 0) |
if (def_timestamp_timeout < 0 && sb.st_mtime != 0) |
status = TS_CURRENT; |
status = TS_CURRENT; |
else { |
else { |
|
/* XXX - should use timespec here */ |
now = time(NULL); |
now = time(NULL); |
if (def_ival(I_TIMESTAMP_TIMEOUT) && |
if (def_timestamp_timeout && |
now - sb.st_mtime < 60 * def_ival(I_TIMESTAMP_TIMEOUT)) { |
now - sb.st_mtime < 60 * def_timestamp_timeout) { |
/* |
/* |
* Check for bogus time on the stampfile. The clock may |
* Check for bogus time on the stampfile. The clock may |
* have been set back or someone could be trying to spoof us. |
* have been set back or someone could be trying to spoof us. |
*/ |
*/ |
if (sb.st_mtime > now + 60 * def_ival(I_TIMESTAMP_TIMEOUT) * 2) { |
if (sb.st_mtime > now + 60 * def_timestamp_timeout * 2) { |
log_error(NO_EXIT, |
log_error(NO_EXIT, |
"timestamp too far in the future: %20.20s", |
"timestamp too far in the future: %20.20s", |
4 + ctime(&sb.st_mtime)); |
4 + ctime(&sb.st_mtime)); |
|
|
remove_timestamp(remove) |
remove_timestamp(remove) |
int remove; |
int remove; |
{ |
{ |
char *timestampdir; |
struct timespec ts; |
char *timestampfile; |
char *timestampdir, *timestampfile, *path; |
char *ts; |
|
int status; |
int status; |
|
|
build_timestamp(×tampdir, ×tampfile); |
build_timestamp(×tampdir, ×tampfile); |
status = timestamp_status(timestampdir, timestampfile, user_name, FALSE); |
status = timestamp_status(timestampdir, timestampfile, user_name, FALSE); |
if (status == TS_OLD || status == TS_CURRENT) { |
if (status == TS_OLD || status == TS_CURRENT) { |
ts = timestampfile ? timestampfile : timestampdir; |
path = timestampfile ? timestampfile : timestampdir; |
if (remove) { |
if (remove) { |
if (timestampfile) |
if (timestampfile) |
status = unlink(timestampfile); |
status = unlink(timestampfile); |
|
|
status = rmdir(timestampdir); |
status = rmdir(timestampdir); |
if (status == -1 && errno != ENOENT) { |
if (status == -1 && errno != ENOENT) { |
log_error(NO_EXIT, "can't remove %s (%s), will reset to Epoch", |
log_error(NO_EXIT, "can't remove %s (%s), will reset to Epoch", |
ts, strerror(errno)); |
path, strerror(errno)); |
remove = FALSE; |
remove = FALSE; |
} |
} |
|
} else { |
|
timespecclear(&ts); |
|
if (touch(-1, path, &ts) == -1) |
|
err(1, "can't reset %s to Epoch", path); |
} |
} |
if (!remove && touch(ts, 0) == -1) |
|
err(1, "can't reset %s to Epoch", ts); |
|
} |
} |
|
|
free(timestampdir); |
free(timestampdir); |