Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "remoting/host/user_auth_pam.h" | |
| 6 | |
| 7 #include <security/pam_appl.h> | |
| 8 #include <stdlib.h> | |
| 9 #include <string> | |
| 10 | |
| 11 namespace remoting { | |
| 12 | |
| 13 // Private Implementation pattern to avoid leaking PAM types, and the | |
| 14 // conversation function, into the header file. | |
| 15 class UserAuthPamPimpl { | |
| 16 public: | |
| 17 explicit UserAuthPamPimpl(UserAuthPam* user_auth_pam); | |
| 18 ~UserAuthPamPimpl(); | |
| 19 bool Authenticate(const std::string& username, | |
| 20 const std::string& password); | |
| 21 | |
| 22 private: | |
| 23 // Conversation function passed to PAM as a callback. | |
| 24 static int ConvFunction(int num_msg, | |
| 25 const struct pam_message** msg, | |
| 26 struct pam_response** resp, | |
| 27 void* appdata_ptr); | |
| 28 | |
| 29 UserAuthPam* user_auth_pam_; | |
| 30 pam_handle_t* pam_handle_; | |
| 31 | |
| 32 // Store these for the PAM conversation function. | |
| 33 std::string username_; | |
| 34 std::string password_; | |
| 35 }; | |
| 36 | |
| 37 UserAuthPamPimpl::UserAuthPamPimpl(UserAuthPam* user_auth_pam) | |
| 38 : user_auth_pam_(user_auth_pam) { | |
| 39 } | |
| 40 | |
| 41 UserAuthPamPimpl::~UserAuthPamPimpl() { | |
| 42 } | |
| 43 | |
| 44 bool UserAuthPamPimpl::Authenticate(const std::string& username, | |
| 45 const std::string& password) { | |
| 46 username_ = username; | |
| 47 password_ = password; | |
| 48 struct pam_conv c; | |
| 49 c.conv = ConvFunction; | |
| 50 c.appdata_ptr = static_cast<void*>(this); | |
| 51 // TODO(lambroslambrou): Allow PAM service name to be configurable. | |
| 52 if (pam_start("chromoting", username_.c_str(), &c, &pam_handle_) != | |
| 53 PAM_SUCCESS) { | |
| 54 return false; | |
| 55 } | |
| 56 | |
| 57 // TODO(lambroslambrou): Move to separate thread. | |
| 58 int pam_status = pam_authenticate(pam_handle_, 0); | |
| 59 pam_end(pam_handle_, pam_status); | |
| 60 return pam_status == PAM_SUCCESS; | |
| 61 } | |
| 62 | |
| 63 // static | |
| 64 int UserAuthPamPimpl::ConvFunction(int num_msg, | |
| 65 const struct pam_message** msg, | |
|
awong
2011/02/15 01:05:28
Since this is c++, we don't need to explicitly tag
Lambros
2011/02/15 15:16:35
Done.
| |
| 66 struct pam_response** resp, | |
| 67 void* appdata_ptr) { | |
| 68 if (num_msg <= 0) | |
| 69 return PAM_CONV_ERR; | |
| 70 UserAuthPamPimpl* user_auth = | |
| 71 reinterpret_cast<UserAuthPamPimpl*>(appdata_ptr); | |
| 72 // Must allocate with malloc(), as the calling PAM module will | |
| 73 // release the memory with free(). | |
| 74 struct pam_response* resp_tmp = reinterpret_cast<struct pam_response*>( | |
|
awong
2011/02/15 01:05:28
Use a scoped_ptr_malloc<> to hold the result?
Lambros
2011/02/15 15:16:35
I had a go at this, but I found it more trouble th
| |
| 75 malloc(num_msg * sizeof(struct pam_response))); | |
| 76 if (resp_tmp == NULL) | |
| 77 return PAM_CONV_ERR; | |
| 78 bool raise_error = false; | |
| 79 // On exit from the loop, 'count' will hold the number of initialised items | |
| 80 // that the cleanup code needs to look at, in case of error. | |
| 81 int count; | |
| 82 for (count = 0; count < num_msg; count++) { | |
| 83 // Alias for readability. | |
| 84 struct pam_response& r = resp_tmp[count]; | |
|
awong
2011/02/15 01:05:28
Please avoid single-letter variable names.
Also,
Lambros
2011/02/15 15:16:35
Done.
| |
| 85 r.resp_retcode = 0; | |
| 86 r.resp = NULL; | |
| 87 switch (msg[count]->msg_style) { | |
| 88 case PAM_PROMPT_ECHO_ON: | |
| 89 r.resp = strdup(user_auth->username_.c_str()); | |
| 90 if (r.resp == NULL) | |
| 91 raise_error = true; | |
| 92 break; | |
| 93 case PAM_PROMPT_ECHO_OFF: | |
| 94 r.resp = strdup(user_auth->password_.c_str()); | |
| 95 if (r.resp == NULL) | |
| 96 raise_error = true; | |
| 97 break; | |
| 98 case PAM_TEXT_INFO: | |
| 99 // No response needed, continue with next prompt. | |
|
awong
2011/02/15 01:05:28
Explain why no response is needed...
Lambros
2011/02/15 15:16:35
Done. I've added a little bit more to the comment
| |
| 100 break; | |
| 101 default: | |
| 102 // Unexpected style code, so abort. | |
| 103 raise_error = true; | |
| 104 } | |
| 105 if (raise_error) | |
| 106 break; | |
| 107 } | |
| 108 | |
| 109 if (raise_error) { | |
| 110 // Not passing the response back, so free up any memory used. | |
| 111 for (int n = 0; n < count; n++) { | |
| 112 if (resp_tmp[n].resp) { | |
| 113 free(resp_tmp[n].resp); | |
| 114 } | |
| 115 } | |
| 116 free(resp_tmp); | |
| 117 return PAM_CONV_ERR; | |
| 118 } else { | |
| 119 *resp = resp_tmp; | |
| 120 return PAM_SUCCESS; | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 UserAuthPam::UserAuthPam() : pimpl_(new UserAuthPamPimpl(this)) { | |
| 125 } | |
| 126 | |
| 127 UserAuthPam::~UserAuthPam() { | |
| 128 } | |
| 129 | |
| 130 bool UserAuthPam::Authenticate(const std::string& username, | |
| 131 const std::string& password) { | |
| 132 return pimpl_->Authenticate(username, password); | |
| 133 } | |
| 134 | |
| 135 } // namespace remoting | |
| OLD | NEW |