Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 // | 4 // |
| 5 // This file implements a wrapper to run the virtual me2me session within a | 5 // This file implements a wrapper to run the virtual me2me session within a |
| 6 // proper PAM session. It will generally be run as root and drop privileges to | 6 // proper PAM session. It will generally be run as root and drop privileges to |
| 7 // the specified user before running the me2me session script. | 7 // the specified user before running the me2me session script. |
| 8 | 8 |
| 9 #include <sys/types.h> | 9 #include <sys/types.h> |
| 10 #include <sys/stat.h> | 10 #include <sys/stat.h> |
| (...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 238 | 238 |
| 239 private: | 239 private: |
| 240 pam_handle_t* pam_handle_ = nullptr; | 240 pam_handle_t* pam_handle_ = nullptr; |
| 241 int last_return_code_ = PAM_SUCCESS; | 241 int last_return_code_ = PAM_SUCCESS; |
| 242 | 242 |
| 243 DISALLOW_COPY_AND_ASSIGN(PamHandle); | 243 DISALLOW_COPY_AND_ASSIGN(PamHandle); |
| 244 }; | 244 }; |
| 245 | 245 |
| 246 // Runs the me2me script in a PAM session. Exits the program on failure. | 246 // Runs the me2me script in a PAM session. Exits the program on failure. |
| 247 // If chown_log is true, the owner and group of the file associated with stdout | 247 // If chown_log is true, the owner and group of the file associated with stdout |
| 248 // will be changed to the target user. | 248 // will be changed to the target user. If match_uid is not nullopt, this |
| 249 void ExecuteSession(std::string user, base::StringPiece script_path, | 249 // function will fail if the final user id does not match the one provided. |
| 250 bool chown_log) { | 250 void ExecuteSession(std::string user, |
| 251 base::StringPiece script_path, | |
| 252 bool chown_log, | |
| 253 base::Optional<uid_t> match_uid) { | |
| 251 ////////////////////////////////////////////////////////////////////////////// | 254 ////////////////////////////////////////////////////////////////////////////// |
| 252 // Set up the PAM session | 255 // Set up the PAM session |
| 253 ////////////////////////////////////////////////////////////////////////////// | 256 ////////////////////////////////////////////////////////////////////////////// |
| 254 | 257 |
| 255 PamHandle pam_handle(kPamName, user.c_str(), &kPamConversation); | 258 PamHandle pam_handle(kPamName, user.c_str(), &kPamConversation); |
| 256 CHECK(pam_handle.IsInitialized()) << "Failed to initialize PAM"; | 259 CHECK(pam_handle.IsInitialized()) << "Failed to initialize PAM"; |
| 257 | 260 |
| 258 // Make sure the account is valid and enabled. | 261 // Make sure the account is valid and enabled. |
| 259 pam_handle.CheckReturnCode(pam_handle.AccountManagement(0), "Account check"); | 262 pam_handle.CheckReturnCode(pam_handle.AccountManagement(0), "Account check"); |
| 260 | 263 |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 308 | 311 |
| 309 private: | 312 private: |
| 310 struct passwd* pwinfo_; | 313 struct passwd* pwinfo_; |
| 311 }; | 314 }; |
| 312 | 315 |
| 313 // Fetch pwinfo again, as it may have been invalidated or the user name might | 316 // Fetch pwinfo again, as it may have been invalidated or the user name might |
| 314 // have been remapped. | 317 // have been remapped. |
| 315 pwinfo = getpwnam(user.c_str()); | 318 pwinfo = getpwnam(user.c_str()); |
| 316 PCHECK(pwinfo != nullptr) << "getpwnam failed"; | 319 PCHECK(pwinfo != nullptr) << "getpwnam failed"; |
| 317 | 320 |
| 321 if (match_uid && pwinfo->pw_uid != *match_uid) { | |
| 322 LOG(FATAL) << "PAM remapped username to one with a different user ID."; | |
| 323 } | |
| 324 | |
| 318 if (chown_log) { | 325 if (chown_log) { |
| 319 int result = fchown(STDOUT_FILENO, pwinfo->pw_uid, pwinfo->pw_gid); | 326 int result = fchown(STDOUT_FILENO, pwinfo->pw_uid, pwinfo->pw_gid); |
| 320 PLOG_IF(WARNING, result != 0) << "Failed to change log file owner"; | 327 PLOG_IF(WARNING, result != 0) << "Failed to change log file owner"; |
| 321 } | 328 } |
| 322 | 329 |
| 323 PreExecDelegate pre_exec_delegate(pwinfo); | 330 PreExecDelegate pre_exec_delegate(pwinfo); |
| 324 | 331 |
| 325 base::LaunchOptions launch_options; | 332 base::LaunchOptions launch_options; |
| 326 | 333 |
| 327 // Required to allow suid binaries to function in the session. | 334 // Required to allow suid binaries to function in the session. |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 391 (std::size_t) 0); | 398 (std::size_t) 0); |
| 392 | 399 |
| 393 mode_t mode = umask(0177); | 400 mode_t mode = umask(0177); |
| 394 int fd = mkstemp(logfile); | 401 int fd = mkstemp(logfile); |
| 395 PCHECK(fd != -1); | 402 PCHECK(fd != -1); |
| 396 umask(mode); | 403 umask(mode); |
| 397 | 404 |
| 398 return fd; | 405 return fd; |
| 399 } | 406 } |
| 400 | 407 |
| 408 // Find the username for the current user. If either USER or LOGNAME is set to | |
| 409 // a user matching our real user id, we return that. Otherwise, we use getpwuid | |
| 410 // to attempt a reverse lookup. Note: It's possible to more than user with the | |
| 411 // same user id but different group membership, home directories, et cetera, | |
| 412 // which is why we check USER and LOGNAME first. | |
| 413 std::string FindCurrentUsername() { | |
| 414 uid_t real_id = getuid(); | |
| 415 struct passwd* pwinfo; | |
| 416 for (const char* var : {"USER", "LOGNAME"}) { | |
| 417 const char* value = getenv(var); | |
| 418 if (value) { | |
| 419 pwinfo = getpwnam(value); | |
| 420 // USER and LOGNAME can be overridden, so make sure the value is valid | |
| 421 // and matches the UID of the invoking user. | |
| 422 if (pwinfo && pwinfo->pw_uid == real_id) { | |
| 423 return pwinfo->pw_name; | |
| 424 } | |
| 425 } | |
| 426 } | |
| 427 errno = 0; | |
| 428 pwinfo = getpwuid(real_id); | |
| 429 PCHECK(pwinfo) << "getpwuid failed"; | |
| 430 return pwinfo->pw_name; | |
| 431 } | |
| 432 | |
| 401 // Daemonizes the process. Output is redirected to a file. Exits the program on | 433 // Daemonizes the process. Output is redirected to a file. Exits the program on |
| 402 // failure. | 434 // failure. |
| 403 // | 435 // |
| 404 // This logic is mostly the same as daemonize() in linux_me2me_host.py. Log- | 436 // This logic is mostly the same as daemonize() in linux_me2me_host.py. Log- |
| 405 // file redirection especially should be kept in sync. Note that this does | 437 // file redirection especially should be kept in sync. Note that this does |
| 406 // not currently wait for the host to start successfully before exiting the | 438 // not currently wait for the host to start successfully before exiting the |
| 407 // parent process like the Python script does, as that functionality is | 439 // parent process like the Python script does, as that functionality is |
| 408 // probably not useful at boot, where the wrapper is expected to be used. If | 440 // probably not useful at boot, where the wrapper is expected to be used. If |
| 409 // it turns out to be desired, it can be implemented by setting up a pipe and | 441 // it turns out to be desired, it can be implemented by setting up a pipe and |
| 410 // passing a file descriptor to the Python script. | 442 // passing a file descriptor to the Python script. |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 467 base::FilePath script_path = | 499 base::FilePath script_path = |
| 468 command_line->GetSwitchValuePath(kScriptSwitchName); | 500 command_line->GetSwitchValuePath(kScriptSwitchName); |
| 469 std::string user = command_line->GetSwitchValueNative(kUserSwitchName); | 501 std::string user = command_line->GetSwitchValueNative(kUserSwitchName); |
| 470 bool foreground = command_line->HasSwitch(kForegroundSwitchName); | 502 bool foreground = command_line->HasSwitch(kForegroundSwitchName); |
| 471 | 503 |
| 472 if (script_path.empty()) { | 504 if (script_path.empty()) { |
| 473 std::fputs("The path to the me2me python script is required.\n", stderr); | 505 std::fputs("The path to the me2me python script is required.\n", stderr); |
| 474 std::exit(EXIT_FAILURE); | 506 std::exit(EXIT_FAILURE); |
| 475 } | 507 } |
| 476 | 508 |
| 509 uid_t real_uid = getuid(); | |
|
Jamie
2017/06/16 19:24:23
Please be consistent with the naming of this; real
rkjnsn
2017/06/16 20:28:31
Good catch.
| |
| 510 | |
| 511 if (real_uid == 0 && user.empty()) { | |
| 512 std::fputs("Target user must be specified when run as root.\n", stderr); | |
| 513 std::exit(EXIT_FAILURE); | |
| 514 } else if (real_uid != 0 && !user.empty()) { | |
| 515 std::fputs("Target user may not be specified by non-root users.\n", stderr); | |
| 516 std::exit(EXIT_FAILURE); | |
|
Jamie
2017/06/16 19:24:23
Since you already check LOGNAME and USER and use t
rkjnsn
2017/06/16 20:28:31
user-session isn't really intended to be called di
| |
| 517 } | |
| 518 | |
| 477 if (user.empty()) { | 519 if (user.empty()) { |
| 478 std::fputs("The target user must be specified.\n", stderr); | 520 user = FindCurrentUsername(); |
| 479 std::exit(EXIT_FAILURE); | |
| 480 } | 521 } |
| 481 | 522 |
| 482 if (!foreground) { | 523 if (!foreground) { |
| 483 Daemonize(); | 524 Daemonize(); |
| 484 } | 525 } |
| 485 | 526 |
| 486 ExecuteSession(std::move(user), script_path.value(), !foreground); | 527 ExecuteSession(std::move(user), script_path.value(), !foreground, |
| 528 real_uid != 0 ? base::make_optional(real_uid) : base::nullopt); | |
| 487 } | 529 } |
| OLD | NEW |