1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
|
/************************************************************************
*
* run_init
*
* SYNOPSIS:
*
* This program allows a user to run an /etc/init.d script in the proper context.
*
* USAGE:
*
* run_init <script> <args>
*
* BUILD OPTIONS:
*
* option USE_PAM:
*
* Set the USE_PAM constant if you want to authenticate users via PAM.
* If USE_PAM is not set, users will be authenticated via direct
* access to the shadow password file.
*
* If you decide to use PAM must be told how to handle run_init. A
* good rule-of-thumb might be to tell PAM to handle run_init in the
* same way it handles su, except that you should remove the pam_rootok.so
* entry so that even root must re-authenticate to run the init scripts
* in the proper context.
*
* If you choose not to use PAM, make sure you have a shadow passwd file
* in /etc/shadow. You can use a simlink if your shadow passwd file
* lives in another directory. Example:
* su
* cd /etc
* ln -s /etc/auth/shadow shadow
*
* If you decide not to use PAM, you will also have to make run_init
* setuid root, so that it can read the shadow passwd file.
*
*
*************************************************************************/
#include <stdio.h>
#include <stdlib.h> /* for malloc(), realloc(), free() */
#include <pwd.h> /* for getpwuid() */
#include <sys/types.h> /* to make getuid() and getpwuid() happy */
#include <sys/wait.h> /* for wait() */
#include <sys/stat.h> /* for struct stat and friends */
#include <getopt.h> /* for getopt_long() form of getopt() */
#include <selinux/selinux.h>
#include <selinux/get_default_type.h>
#include <selinux/context.h> /* for context-mangling functions */
#include <fcntl.h>
#include <ctype.h>
#include <limits.h>
#ifdef USE_AUDIT
#include <libaudit.h>
#endif
#ifdef USE_NLS
#include <libintl.h>
#include <locale.h>
#define _(msgid) gettext (msgid)
#else
#define _(msgid) (msgid)
#endif
#ifndef PACKAGE
#define PACKAGE "policycoreutils" /* the name of this package lang translation */
#endif
/* USAGE_STRING describes the command-line args of this program. */
#define USAGE_STRING _("USAGE: run_init <script> <args ...>\n\
where: <script> is the name of the init script to run,\n\
<args ...> are the arguments to that script.")
#define CONTEXT_FILE "initrc_context"
#ifdef USE_PAM
/************************************************************************
*
* All PAM code goes in this section.
*
************************************************************************/
#include <unistd.h> /* for getuid(), exit(), getopt() */
#include <security/pam_appl.h> /* for PAM functions */
#include <security/pam_misc.h> /* for misc_conv PAM utility function */
#define SERVICE_NAME "run_init" /* the name of this program for PAM */
/* The file containing the context to run
* the scripts under. */
int authenticate_via_pam(const struct passwd *);
/* authenticate_via_pam()
*
* in: p_passwd_line - struct containing data from our user's line in
* the passwd file.
* out: nothing
* return: value condition
* ----- ---------
* 1 PAM thinks that the user authenticated themselves properly
* 0 otherwise
*
* This function uses PAM to authenticate the user running this
* program. This is the only function in this program that makes PAM
* calls.
*
*/
int authenticate_via_pam(const struct passwd *p_passwd_line)
{
int result = 0; /* our result, set to 0 (not authenticated) by default */
pam_handle_t *pam_handle; /* opaque handle used by all PAM functions */
/* This is a jump table of functions for PAM to use when it wants to *
* communicate with the user. We'll be using misc_conv(), which is *
* provided for us via pam_misc.h. */
struct pam_conv pam_conversation = {
misc_conv,
NULL
};
/* Make `p_pam_handle' a valid PAM handle so we can use it when *
* calling PAM functions. */
if (PAM_SUCCESS != pam_start(SERVICE_NAME,
p_passwd_line->pw_name,
&pam_conversation, &pam_handle)) {
fprintf(stderr, _("failed to initialize PAM\n"));
exit(-1);
}
/* Ask PAM to authenticate the user running this program */
if (PAM_SUCCESS == pam_authenticate(pam_handle, 0)) {
result = 1; /* user authenticated OK! */
}
/* If we were successful, call pam_acct_mgmt() to reset the
* pam_tally failcount.
*/
if (result && (PAM_SUCCESS != pam_acct_mgmt(pam_handle, 0)) ) {
fprintf(stderr, _("failed to get account information\n"));
exit(-1);
}
/* We're done with PAM. Free `pam_handle'. */
pam_end(pam_handle, PAM_SUCCESS);
return (result);
} /* authenticate_via_pam() */
#else /* else !USE_PAM */
/************************************************************************
*
* All shadow passwd code goes in this section.
*
************************************************************************/
#include <unistd.h> /* for getuid(), exit(), crypt() */
#include <shadow.h> /* for shadow passwd functions */
#include <string.h> /* for strlen(), memset() */
#define PASSWORD_PROMPT _("Password:") /* prompt for getpass() */
int authenticate_via_shadow_passwd(const struct passwd *);
/* authenticate_via_shadow_passwd()
*
* in: p_passwd_line - struct containing data from our user's line in
* the passwd file.
* out: nothing
* return: value condition
* ----- ---------
* 1 user authenticated themselves properly according to the
* shadow passwd file.
* 0 otherwise
*
* This function uses the shadow passwd file to authenticate the user running
* this program.
*
*/
int authenticate_via_shadow_passwd(const struct passwd *p_passwd_line)
{
struct spwd *p_shadow_line; /* struct derived from shadow passwd file line */
char *unencrypted_password_s; /* unencrypted password input by user */
char *encrypted_password_s; /* user's password input after being crypt()ed */
/* Make `p_shadow_line' point to the data from the current user's *
* line in the shadow passwd file. */
setspent(); /* Begin access to the shadow passwd file. */
p_shadow_line = getspnam(p_passwd_line->pw_name);
endspent(); /* End access to the shadow passwd file. */
if (!(p_shadow_line)) {
fprintf(stderr,
_
("Cannot find your entry in the shadow passwd file.\n"));
exit(-1);
}
/* Ask user to input unencrypted password */
if (!(unencrypted_password_s = getpass(PASSWORD_PROMPT))) {
fprintf(stderr, _("getpass cannot open /dev/tty\n"));
exit(-1);
}
/* Use crypt() to encrypt user's input password. Clear the *
* unencrypted password as soon as we're done, so it is not *
* visible to memory snoopers. */
encrypted_password_s = crypt(unencrypted_password_s,
p_shadow_line->sp_pwdp);
memset(unencrypted_password_s, 0, strlen(unencrypted_password_s));
/* Return 1 (authenticated) iff the encrypted version of the user's *
* input password matches the encrypted password stored in the *
* shadow password file. */
return (!strcmp(encrypted_password_s, p_shadow_line->sp_pwdp));
} /* authenticate_via_shadow_passwd() */
#endif /* if/else USE_PAM */
/*
* authenticate_user()
*
* Authenticate the user.
*
* in: nothing
* out: nothing
* return: 0 When success
* -1 When failure
*/
int authenticate_user(void)
{
#define INITLEN 255
struct passwd *p_passwd_line; /* struct derived from passwd file line */
uid_t uid;
/*
* Determine the Linux user identity to re-authenticate.
* If supported and set, use the login uid, as this should be more stable.
* Otherwise, use the real uid.
* The SELinux user identity is no longer used, as Linux users are now
* mapped to SELinux users via seusers and the SELinux user identity space
* is separate.
*/
#ifdef USE_AUDIT
uid = audit_getloginuid();
if (uid == (uid_t) - 1)
uid = getuid();
#else
uid = getuid();
#endif
p_passwd_line = getpwuid(uid);
if (!p_passwd_line) {
fprintf(stderr, "cannot find your entry in the passwd file.\n");
return (-1);
}
printf("Authenticating %s.\n", p_passwd_line->pw_name);
/*
* Re-authenticate the user running this program.
* This is just to help confirm user intent (vs. invocation by
* malicious software), not to authorize the operation (which is covered
* by policy). Trusted path mechanism would be preferred.
*/
#ifdef USE_PAM
if (!authenticate_via_pam(p_passwd_line)) {
#else /* !USE_PAM */
if (!authenticate_via_shadow_passwd(p_passwd_line)) {
#endif /* if/else USE_PAM */
fprintf(stderr, _("run_init: incorrect password for %s\n"),
p_passwd_line->pw_name);
return (-1);
}
/* If we reach here, then we have authenticated the user. */
#ifdef CANTSPELLGDB
printf("You are authenticated!\n");
#endif
return 0;
} /* authenticate_user() */
/*
* get_init_context()
*
* Get the CONTEXT associated with the context for the init scripts. *
*
* in: nothing
* out: The CONTEXT associated with the context.
* return: 0 on success, -1 on failure.
*/
int get_init_context(security_context_t * context)
{
FILE *fp;
char buf[255], *bufp;
int buf_len;
char context_file[PATH_MAX];
snprintf(context_file, sizeof(context_file) - 1, "%s/%s",
selinux_contexts_path(), CONTEXT_FILE);
fp = fopen(context_file, "r");
if (!fp) {
fprintf(stderr, _("Could not open file %s\n"), context_file);
return -1;
}
while (1) { /* loop until we find a non-empty line */
if (!fgets(buf, sizeof buf, fp))
break;
buf_len = strlen(buf);
if (buf[buf_len - 1] == '\n')
buf[buf_len - 1] = 0;
bufp = buf;
while (*bufp && isspace(*bufp))
bufp++;
if (*bufp) {
*context = strdup(bufp);
if (!(*context))
goto out;
fclose(fp);
return 0;
}
}
out:
fclose(fp);
fprintf(stderr, _("No context in file %s\n"), context_file);
return -1;
} /* get_init_context() */
/*****************************************************************************
* main() *
*****************************************************************************/
int main(int argc, char *argv[])
{
extern char *optarg; /* used by getopt() for arg strings */
extern int opterr; /* controls getopt() error messages */
security_context_t new_context; /* context for the init script context */
#ifdef USE_NLS
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
#endif
/* Verify that we are running on a flask-enabled kernel. */
if (!is_selinux_enabled()) {
fprintf(stderr,
_
("Sorry, run_init may be used only on a SELinux kernel.\n"));
exit(-1);
}
/*
* Step 1: Handle command-line arguments. The first argument is the
* name of the script to run. All other arguments are for the script
* itself, and will be passed directly to the script.
*/
if (argc < 2) {
fprintf(stderr, "%s\n", USAGE_STRING);
exit(-1);
}
/*
* Step 2: Authenticate the user.
*/
if (authenticate_user() != 0) {
fprintf(stderr, _("authentication failed.\n"));
exit(-1);
}
/*
* Step 3: Get the context for the script to be run in.
*/
if (get_init_context(&new_context) == 0) {
#ifdef CANTSPELLGDB
printf("context is %s\n", new_context);
#endif
} else {
exit(-1);
}
/*
* Step 4: Run the command in the correct context.
*/
if (chdir("/")) {
perror("chdir");
exit(-1);
}
if (setexeccon(new_context) < 0) {
fprintf(stderr, _("Could not set exec context to %s.\n"),
new_context);
exit(-1);
}
if (access("/usr/sbin/open_init_pty", X_OK) != 0) {
if (execvp(argv[1], argv + 1)) {
perror("execvp");
exit(-1);
}
return 0;
}
/*
* Do not execvp the command directly from run_init; since it would run
* under with a pty under sysadm_devpts_t. Instead, we call open_init_tty,
* which transitions us into initrc_t, which then spawns a new
* process, that gets a pty with context initrc_devpts_t. Just
* execvp or using a exec(1) recycles pty's, and does not open a new
* one.
*/
if (execvp("/usr/sbin/open_init_pty", argv)) {
perror("execvp");
exit(-1);
}
return 0;
} /* main() */
|