src/include/oslogin_utils.h (138 lines of code) (raw):

// Copyright 2017 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include <grp.h> #include <pthread.h> #include <pwd.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <syslog.h> #include <string> #include <vector> #define TOTP "TOTP" #define AUTHZEN "AUTHZEN" #define INTERNAL_TWO_FACTOR "INTERNAL_TWO_FACTOR" #define IDV_PREREGISTERED_PHONE "IDV_PREREGISTERED_PHONE" #define SECURITY_KEY_OTP "SECURITY_KEY_OTP" #define INITGROUP_CACHE_EXPIRE_SECONDS 500 #ifdef DEBUG #undef DEBUG #define DEBUG(fmt, ...) \ do { \ openlog("nss_oslogin", LOG_PID|LOG_PERROR, LOG_DAEMON); \ syslog(LOG_ERR, fmt, ##__VA_ARGS__); \ closelog(); \ } while (0) #else #define DEBUG(fmt, ...) \ do { \ } while (0) #endif /* DEBUG */ using std::string; using std::vector; namespace oslogin_utils { // Metadata server URL. static const char kMetadataServerUrl[] = "http://169.254.169.254/computeMetadata/v1/oslogin/"; // BufferManager encapsulates and manages a buffer and length. This class is not // thread safe. class BufferManager { public: // Create a BufferManager that will dole out chunks of buf as requested. BufferManager(char* buf, size_t buflen); // Copies a string to the buffer and sets the buffer to point to that // string. Copied string is guaranteed to be null-terminated. // Returns false and sets errnop if there is not enough space left in the // buffer for the string. bool AppendString(const string& value, char** buffer, int* errnop); // Return a pointer to a buffer of size bytes. Returns NULL and sets errnop to // ERANGE if there is not enough space left in the buffer for the request. void* Reserve(size_t bytes, int* errnop); private: // Whether there is space available in the buffer. bool CheckSpaceAvailable(size_t bytes_to_write) const; char* buf_; size_t buflen_; // Not copyable or assignable. BufferManager& operator=(const BufferManager&); BufferManager(const BufferManager&); }; // Challenge represents a security challenge available to the user. class Challenge { public: int id; string type; string status; }; class Group { public: int64_t gid; string name; }; // NssCache caches passwd entries for getpwent_r. This is used to prevent making // an HTTP call on every getpwent_r invocation. Stores up to cache_size entries // at a time. This class is not thread safe. class NssCache { public: explicit NssCache(int cache_size); // Clears and resets the NssCache. void Reset(); // Whether the cache has a next entry. bool HasNextEntry(); // Whether the cache has reached the last page of the database. bool OnLastPage() { return on_last_page_; } // Grabs the next passwd or group entry. Returns true on success. Sets errnop on // failure. bool GetNextPasswd(BufferManager* buf, struct passwd* result, int* errnop); bool GetNextGroup(BufferManager* buf, struct group* result, int* errnop); // Loads a json array of passwd or group entries in the cache, starting at the // beginning of the cache. This will remove all previous entries in the cache. // response is expected to be a JSON array of passwd or group entries. Returns // true on success. bool LoadJsonUsersToCache(string response); bool LoadJsonGroupsToCache(string response, int* errnop); // Helper method for get(pw|gr)ent nss methods. Each call will iterate through the // OsLogin database and return the next entry. Internally, the cache will // keep track of pages of user or group entries, and will make an http call to // the server if necessary to retrieve additional entries. Returns whether // retrieval was successful. If true, the result will contain // valid data. bool NssGetpwentHelper(BufferManager* buf, struct passwd* result, int* errnop); bool NssGetgrentHelper(BufferManager* buf, struct group* result, int* errnop); // Returns the page token for requesting the next page of entries. string GetPageToken() { return page_token_; } private: // The maximum size of the cache. int cache_size_; // Vector of entries. These are represented as stringified json object. std::vector<std::string> entry_cache_; // The page token for requesting the next page of entries. std::string page_token_; // Index for requesting the next entry from the cache. uint32_t index_; // Whether the NssCache has reached the last page of the database. bool on_last_page_; // Not copyable or assignable. NssCache& operator=(const NssCache&); NssCache(const NssCache&); }; // Auto locks and unlocks a given mutex on construction/destruction. Does NOT // take ownership of the mutex. class MutexLock { public: explicit MutexLock(pthread_mutex_t* mutex) : mutex_(mutex) { pthread_mutex_lock(mutex_); } ~MutexLock() { pthread_mutex_unlock(mutex_); } private: // The mutex to lock/unlock pthread_mutex_t* const mutex_; // Not copyable or assignable. MutexLock& operator=(const MutexLock); MutexLock(const MutexLock&); }; // Callback invoked when Curl completes a request. size_t OnCurlWrite(void* buf, size_t size, size_t nmemb, void* userp); // Uses Curl to issue a GET request to the given url. Returns whether the // request was successful. If successful, the result from the server will be // stored in response, and the HTTP response code will be stored in http_code. bool HttpGet(const string& url, string* response, long* http_code); bool HttpPost(const string& url, const string& data, string* response, long* http_code); // Based on known MDS status codes returns whether the HTTP request // should be retried or not. bool ShouldRetry(long http_code); // Returns whether user_name is a valid OsLogin user name. bool ValidateUserName(const string& user_name); // URL encodes the given parameter. Returns the encoded parameter. std::string UrlEncode(const string& param); // Returns true if the given passwd contains valid fields. If pw_dir, pw_shell, // or pw_passwd are not set, this will populate these entries with default // values. bool ValidatePasswd(struct passwd* result, BufferManager* buf, int* errnop); // Adds users and associated array of char* to provided buffer and store pointer // to array in result.gr_mem. bool AddUsersToGroup(std::vector<string> users, struct group* result, BufferManager* buf, int* errnop); // Iterates through all groups until one matching provided group is found, // replacing gr_name with a buffermanager provided string. bool FindGroup(struct group* grp, BufferManager* buf, int* errnop); // Iterates through all users for a group, storing results in a provided string // vector. bool GetUsersForGroup(string groupname, std::vector<string>* users, int* errnop); // Iterates through all groups for a user, storing results in a provided string // vector. bool GetGroupsForUser(string username, std::vector<Group>* groups, int* errnop); // Parses a JSON groups response, storing results in a provided Group vector. bool ParseJsonToGroups(const string& json, std::vector<Group>* groups); // Parses a JSON users response, storing results in a provided string vector. bool ParseJsonToUsers(const string& json, std::vector<string>* users); // Gets group matching name. bool GetGroupByName(string name, struct group* grp, BufferManager* buf, int* errnop); // Gets group matching GID. bool GetGroupByGID(uint32_t gid, struct group* grp, BufferManager* buf, int* errnop); // Iterates through all users for a group, storing results in a provided string vector. bool GetUsersForGroup(string groupname, std::vector<string>* users, int* errnop); // Iterates through all groups for a user, storing results in a provided string vector. bool GetGroupsForUser(string username, std::vector<Group>* groups, int* errnop); // Parses a JSON groups response, storing results in a provided Group vector. bool ParseJsonToGroups(const string& json, std::vector<Group>* groups); // Parses a JSON users response, storing results in a provided string vector. bool ParseJsonToUsers(const string& json, std::vector<string> *users); // Parses a JSON LoginProfiles response for SSH keys. Returns a vector of valid // ssh_keys. A key is considered valid if it's expiration date is greater than // current unix time. std::vector<string> ParseJsonToSshKeys(const string& json); std::vector<string> ParseJsonToSshKeysSk(const string& json); // Parses a JSON object and returns the value associated with a given key. bool ParseJsonToKey(const string& json, const string& key, string* response); // Parses a JSON LoginProfiles response and returns the email under the "name" // field. bool ParseJsonToEmail(const string& json, string* email); // Parses a JSON LoginProfiles response and populates the passwd struct with the // corresponding values set in the JSON object. Returns whether the parse was // successful or not. If unsuccessful, errnop will also be set. bool ParseJsonToPasswd(const string& response, struct passwd* result, BufferManager* buf, int* errnop); bool ParseJsonToGroup(const string& response, struct group* result, BufferManager* buf, int* errnop); // Parses a JSON adminLogin or login response and returns whether the user has // the requested privilege. bool ParseJsonToSuccess(const string& json); // Parses a JSON startSession response into a vector of Challenge objects. bool ParseJsonToChallenges(const string& json, vector<Challenge>* challenges); // Calls the startSession API. bool StartSession(const string& email, string* response); // Calls the continueSession API. bool ContinueSession(bool alt, const string& email, const string& user_token, const string& session_id, const Challenge& challenge, string* response); // Returns user information from the metadata server. bool GetUser(const string& username, string* response); // Initializes the global sys logger instance setting it up with the // provided ident and app, so the syslog entries will look like: // <<ident>>: <<app>>: <<Message>> // For google_authorized_keys for example, it would look like: // sshd: google_authorized_keys: <<Message>> extern void SetupSysLog(const char *ident, const char *app); // Closes the sys logger. extern void CloseSysLog(); // Prints out to sys logger with ERR severity. extern void SysLogErr(const char *fmt, ...); // AuthoOptions wraps authorization options. struct AuthOptions { // admin_policy_required determines if a user is only authorized if admin // policy is available for such a user. i.e. AuthorizeUser() should return // false if adminLogin is not available. bool admin_policy_required; // security_key determines if the MDS "/users?..." should use // the view=securityKey parameter. bool security_key; // fingerprint is used when authorizing certificate based // authentication sessions. char *fingerprint; // fp_len is the fingerprint string length; size_t fp_len; }; // Perform user authorization logic & create users files and google sudoers, returns true if successful, // and false otherwise. bool AuthorizeUser(const char *user_name, struct AuthOptions opts, string *user_response); // Given a file_path extracts the file name only. file_path must be a null terminated string. const char *FileName(const char *file_path); } // namespace oslogin_utils