| 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 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 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. |
| 249 void ExecuteSession(std::string user, base::StringPiece script_path, | 249 void ExecuteSession(std::string user, base::StringPiece script_path, |
| 250 bool chown_log) { | 250 bool chown_log) { |
| 251 ////////////////////////////////////////////////////////////////////////////// | 251 ////////////////////////////////////////////////////////////////////////////// |
| 252 // Set up the PAM session | 252 // Set up the PAM session |
| 253 ////////////////////////////////////////////////////////////////////////////// | 253 ////////////////////////////////////////////////////////////////////////////// |
| 254 | 254 |
| 255 PamHandle pam_handle(kPamName, user.c_str(), &kPamConversation); | 255 PamHandle pam_handle(kPamName, user.c_str(), &kPamConversation); |
| 256 CHECK(pam_handle.IsInitialized()) << "Failed to initialize PAM"; | 256 // Failed to initialize PAM |
| 257 CHECK(pam_handle.IsInitialized()); |
| 257 | 258 |
| 258 // Make sure the account is valid and enabled. | 259 // Make sure the account is valid and enabled. |
| 259 pam_handle.CheckReturnCode(pam_handle.AccountManagement(0), "Account check"); | 260 pam_handle.CheckReturnCode(pam_handle.AccountManagement(0), "Account check"); |
| 260 | 261 |
| 261 // PAM may remap the user at any stage. | 262 // PAM may remap the user at any stage. |
| 262 user = pam_handle.GetUser().value_or(std::move(user)); | 263 user = pam_handle.GetUser().value_or(std::move(user)); |
| 263 | 264 |
| 264 // setcred explicitly does not handle group membership, and specifies that | 265 // setcred explicitly does not handle group membership, and specifies that |
| 265 // group membership should be established before calling setcred. PAM modules | 266 // group membership should be established before calling setcred. PAM modules |
| 266 // may also use getpwnam, so pwinfo can only be assumed valid until the next | 267 // may also use getpwnam, so pwinfo can only be assumed valid until the next |
| 267 // PAM call. | 268 // PAM call. |
| 268 errno = 0; | 269 errno = 0; |
| 269 struct passwd* pwinfo = getpwnam(user.c_str()); | 270 struct passwd* pwinfo = getpwnam(user.c_str()); |
| 270 PCHECK(pwinfo != nullptr) << "getpwnam failed"; | 271 // getpwnam failed |
| 271 PCHECK(setgid(pwinfo->pw_gid) == 0) << "setgid failed"; | 272 CHECK(pwinfo != nullptr); |
| 272 PCHECK(initgroups(pwinfo->pw_name, pwinfo->pw_gid) == 0) | 273 // setgid failed |
| 273 << "initgroups failed"; | 274 CHECK(setgid(pwinfo->pw_gid) == 0); |
| 275 // initgroups failed |
| 276 CHECK(initgroups(pwinfo->pw_name, pwinfo->pw_gid) == 0); |
| 274 | 277 |
| 275 // The documentation states that setcred should be called before open_session, | 278 // The documentation states that setcred should be called before open_session, |
| 276 // as done here, but it may be worth noting that `login` calls open_session | 279 // as done here, but it may be worth noting that `login` calls open_session |
| 277 // first. | 280 // first. |
| 278 pam_handle.CheckReturnCode(pam_handle.SetCredentials(PAM_ESTABLISH_CRED), | 281 pam_handle.CheckReturnCode(pam_handle.SetCredentials(PAM_ESTABLISH_CRED), |
| 279 "Set credentials"); | 282 "Set credentials"); |
| 280 | 283 |
| 281 pam_handle.CheckReturnCode(pam_handle.OpenSession(0), "Open session"); | 284 pam_handle.CheckReturnCode(pam_handle.OpenSession(0), "Open session"); |
| 282 | 285 |
| 283 // The above may have remapped the user. | 286 // The above may have remapped the user. |
| 284 user = pam_handle.GetUser().value_or(std::move(user)); | 287 user = pam_handle.GetUser().value_or(std::move(user)); |
| 285 | 288 |
| 286 base::Optional<base::EnvironmentMap> pam_environment = | 289 base::Optional<base::EnvironmentMap> pam_environment = |
| 287 pam_handle.GetEnvironment(); | 290 pam_handle.GetEnvironment(); |
| 288 CHECK(pam_environment) << "Failed to get environment from PAM"; | 291 // Failed to get environment from PAM |
| 292 CHECK(pam_environment); |
| 289 | 293 |
| 290 ////////////////////////////////////////////////////////////////////////////// | 294 ////////////////////////////////////////////////////////////////////////////// |
| 291 // Prepare to execute remoting session process | 295 // Prepare to execute remoting session process |
| 292 ////////////////////////////////////////////////////////////////////////////// | 296 ////////////////////////////////////////////////////////////////////////////// |
| 293 | 297 |
| 294 // Callback to be run in child process after fork and before exec. | 298 // Callback to be run in child process after fork and before exec. |
| 295 // chdir is called manually instead of using LaunchOptions.current_directory | 299 // chdir is called manually instead of using LaunchOptions.current_directory |
| 296 // because it should take place after setuid. (This both makes sure the user | 300 // because it should take place after setuid. (This both makes sure the user |
| 297 // has the proper permissions and also apparently avoids some obscure errors | 301 // has the proper permissions and also apparently avoids some obscure errors |
| 298 // that can occur when accessing some network filesystems as the wrong user.) | 302 // that can occur when accessing some network filesystems as the wrong user.) |
| 299 class PreExecDelegate : public base::LaunchOptions::PreExecDelegate { | 303 class PreExecDelegate : public base::LaunchOptions::PreExecDelegate { |
| 300 public: | 304 public: |
| 301 void RunAsyncSafe() override { | 305 void RunAsyncSafe() override { |
| 302 // Use RAW_CHECK to avoid allocating post-fork. | 306 // Use RAW_CHECK to avoid allocating post-fork. |
| 303 RAW_CHECK(setuid(pwinfo_->pw_uid) == 0); | 307 RAW_CHECK(setuid(pwinfo_->pw_uid) == 0); |
| 304 RAW_CHECK(chdir(pwinfo_->pw_dir) == 0); | 308 RAW_CHECK(chdir(pwinfo_->pw_dir) == 0); |
| 305 } | 309 } |
| 306 | 310 |
| 307 PreExecDelegate(struct passwd* pwinfo) : pwinfo_(pwinfo) {} | 311 PreExecDelegate(struct passwd* pwinfo) : pwinfo_(pwinfo) {} |
| 308 | 312 |
| 309 private: | 313 private: |
| 310 struct passwd* pwinfo_; | 314 struct passwd* pwinfo_; |
| 311 }; | 315 }; |
| 312 | 316 |
| 313 // Fetch pwinfo again, as it may have been invalidated or the user name might | 317 // Fetch pwinfo again, as it may have been invalidated or the user name might |
| 314 // have been remapped. | 318 // have been remapped. |
| 315 pwinfo = getpwnam(user.c_str()); | 319 pwinfo = getpwnam(user.c_str()); |
| 316 PCHECK(pwinfo != nullptr) << "getpwnam failed"; | 320 // getpwnam failed |
| 321 CHECK(pwinfo != nullptr); |
| 317 | 322 |
| 318 if (chown_log) { | 323 if (chown_log) { |
| 319 int result = fchown(STDOUT_FILENO, pwinfo->pw_uid, pwinfo->pw_gid); | 324 int result = fchown(STDOUT_FILENO, pwinfo->pw_uid, pwinfo->pw_gid); |
| 320 PLOG_IF(WARNING, result != 0) << "Failed to change log file owner"; | 325 PLOG_IF(WARNING, result != 0) << "Failed to change log file owner"; |
| 321 } | 326 } |
| 322 | 327 |
| 323 PreExecDelegate pre_exec_delegate(pwinfo); | 328 PreExecDelegate pre_exec_delegate(pwinfo); |
| 324 | 329 |
| 325 base::LaunchOptions launch_options; | 330 base::LaunchOptions launch_options; |
| 326 | 331 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 342 launch_options.pre_exec_delegate = &pre_exec_delegate; | 347 launch_options.pre_exec_delegate = &pre_exec_delegate; |
| 343 | 348 |
| 344 // By convention, a login shell is signified by preceeding the shell name in | 349 // By convention, a login shell is signified by preceeding the shell name in |
| 345 // argv[0] with a '-'. | 350 // argv[0] with a '-'. |
| 346 base::CommandLine command_line(base::FilePath( | 351 base::CommandLine command_line(base::FilePath( |
| 347 '-' + base::FilePath(pwinfo->pw_shell).BaseName().value())); | 352 '-' + base::FilePath(pwinfo->pw_shell).BaseName().value())); |
| 348 | 353 |
| 349 base::Optional<std::string> escaped_script_path = | 354 base::Optional<std::string> escaped_script_path = |
| 350 ShellEscapeArgument(script_path); | 355 ShellEscapeArgument(script_path); |
| 351 | 356 |
| 352 CHECK(escaped_script_path) << "Could not escape script path"; | 357 // Could not escape script path |
| 358 CHECK(escaped_script_path); |
| 353 | 359 |
| 354 command_line.AppendSwitch("-c"); | 360 command_line.AppendSwitch("-c"); |
| 355 command_line.AppendArg(*escaped_script_path + | 361 command_line.AppendArg(*escaped_script_path + |
| 356 " --start --foreground --keep-parent-env"); | 362 " --start --foreground --keep-parent-env"); |
| 357 | 363 |
| 358 // Tell LaunchProcess where to find the executable, since argv[0] doesn't | 364 // Tell LaunchProcess where to find the executable, since argv[0] doesn't |
| 359 // point to it. | 365 // point to it. |
| 360 launch_options.real_path = base::FilePath(pwinfo->pw_shell); | 366 launch_options.real_path = base::FilePath(pwinfo->pw_shell); |
| 361 | 367 |
| 362 ////////////////////////////////////////////////////////////////////////////// | 368 ////////////////////////////////////////////////////////////////////////////// |
| 363 // We're ready to execute the remoting session | 369 // We're ready to execute the remoting session |
| 364 ////////////////////////////////////////////////////////////////////////////// | 370 ////////////////////////////////////////////////////////////////////////////// |
| 365 | 371 |
| 366 base::Process child = base::LaunchProcess(command_line, launch_options); | 372 base::Process child = base::LaunchProcess(command_line, launch_options); |
| 367 | 373 |
| 368 if (child.IsValid()) { | 374 if (child.IsValid()) { |
| 369 int exit_code = 0; | 375 int exit_code = 0; |
| 370 // Die if wait fails so we don't close the PAM session while the child is | 376 // Die if wait fails so we don't close the PAM session while the child is |
| 371 // still running. | 377 // still running. |
| 372 CHECK(child.WaitForExit(&exit_code)) << "Failed to wait for child process"; | 378 // Failed to wait for child process |
| 379 CHECK(child.WaitForExit(&exit_code)); |
| 373 LOG_IF(WARNING, exit_code != 0) << "Child did not exit normally"; | 380 LOG_IF(WARNING, exit_code != 0) << "Child did not exit normally"; |
| 374 } | 381 } |
| 375 | 382 |
| 376 // Best effort PAM cleanup | 383 // Best effort PAM cleanup |
| 377 if (pam_handle.CloseSession(0) != PAM_SUCCESS) { | 384 if (pam_handle.CloseSession(0) != PAM_SUCCESS) { |
| 378 LOG(WARNING) << "Failed to close PAM session"; | 385 LOG(WARNING) << "Failed to close PAM session"; |
| 379 } | 386 } |
| 380 ignore_result(pam_handle.SetCredentials(PAM_DELETE_CRED)); | 387 ignore_result(pam_handle.SetCredentials(PAM_DELETE_CRED)); |
| 381 } | 388 } |
| 382 | 389 |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 478 std::fputs("The target user must be specified.\n", stderr); | 485 std::fputs("The target user must be specified.\n", stderr); |
| 479 std::exit(EXIT_FAILURE); | 486 std::exit(EXIT_FAILURE); |
| 480 } | 487 } |
| 481 | 488 |
| 482 if (!foreground) { | 489 if (!foreground) { |
| 483 Daemonize(); | 490 Daemonize(); |
| 484 } | 491 } |
| 485 | 492 |
| 486 ExecuteSession(std::move(user), script_path.value(), !foreground); | 493 ExecuteSession(std::move(user), script_path.value(), !foreground); |
| 487 } | 494 } |
| OLD | NEW |