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