diff -Nru google-compute-engine-oslogin-20220714.00/.gitignore google-compute-engine-oslogin-20231004.00/.gitignore --- google-compute-engine-oslogin-20220714.00/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/.gitignore 2023-10-04 22:45:15.000000000 +0000 @@ -0,0 +1,12 @@ +*.so +*.o +google_authorized_keys +google_authorized_keys_sk +google_oslogin_nss_cache +test/sshca_runner +test/test_detail.xml +selinux/oslogin.mod +selinux/oslogin.pp +test/new_test_runner +test/test_runner +src/google_authorized_principals diff -Nru google-compute-engine-oslogin-20220714.00/Makefile google-compute-engine-oslogin-20231004.00/Makefile --- google-compute-engine-oslogin-20220714.00/Makefile 2022-07-12 17:21:31.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/Makefile 2023-10-04 22:45:15.000000000 +0000 @@ -1,20 +1,49 @@ -all install : +.PHONY: all clean install +.PHONY: prowbuild prowtest +.PHONY: alltests non_network_tests network_tests + +.DEFAULT_GOAL := all + +all install: selinux_module $(MAKE) -C src $@ -alltests non_network_tests network_tests : +selinux_module: +ifdef INSTALL_SELINUX + $(MAKE) -C selinux +else + @echo "Variable INSTALL_SELINUX not defined, skipping selinux module build." +endif + +alltests non_network_tests network_tests: $(MAKE) -C test $@ -clean : +clean: $(MAKE) -C src clean $(MAKE) -C test clean + rm -f debian_deps debian_build_deps debian_test_deps + rm -f rhel_deps rhel_build_deps + +prowbuild: debian_build_deps all -prowbuild : debian_deps all +prowtest: debian_deps non_network_tests + mv -f test/test_detail.xml ${ARTIFACTS}/junit.xml -prowtest : debian_deps non_network_tests - mv test/test_detail.xml ${ARTIFACTS}/junit.xml +debian_deps: debian_build_deps debian_test_deps + touch $@ -debian_deps : +debian_build_deps: apt-get -y install g++ libcurl4-openssl-dev libjson-c-dev libpam-dev \ - googletest && touch $@ + && touch $@ -.PHONY : all clean install prowbuild prowtest alltests non_network_tests network_tests +debian_test_deps: + apt-get -y install googletest \ + && touch $@ + +rhel_deps: rhel_build_deps + touch $@ + +rhel_build_deps: + dnf config-manager --set-enabled crb \ + && dnf install -y policycoreutils gcc-c++ boost-devel libcurl-devel \ + json-c-devel pam-devel policycoreutils \ + && touch $@ diff -Nru google-compute-engine-oslogin-20220714.00/OWNERS google-compute-engine-oslogin-20231004.00/OWNERS --- google-compute-engine-oslogin-20220714.00/OWNERS 2022-07-12 17:21:31.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/OWNERS 2023-10-04 22:45:15.000000000 +0000 @@ -2,9 +2,15 @@ # See the OWNERS docs at https://go.k8s.io/owners approvers: - - hopkiw - - illfelder -reviewers: - - hopkiw - - illfelder - - jaiminsh + - a-crate + - bkatyl + - chaitanyakulkarni28 + - dorileo + - drewhli + - elicriffield + - jjerger + - karnvadaliya + - koln67 + - quintonamore + - vorakl + - zmarano diff -Nru google-compute-engine-oslogin-20220714.00/README.md google-compute-engine-oslogin-20231004.00/README.md --- google-compute-engine-oslogin-20220714.00/README.md 2022-07-12 17:21:31.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/README.md 2023-10-04 22:45:15.000000000 +0000 @@ -11,11 +11,8 @@ * [NSS Modules](#nss-modules) * [PAM Modules](#pam-modules) * [Utilities](#Utilities) - * [Control Script](#control-script) * [SELinux Policy](#selinux-policy) * [Source Packages](#source-packages) - * [DEB](#deb) - * [RPM](#rpm) ## Overview @@ -136,21 +133,6 @@ ## Utilities -#### Control Script - -The `google_oslogin_control` shell script activates or deactivates the OS Login -features. It is invoked by the google accounts daemon. The control file performs -the following tasks: - -* Adds (or removes) AuthorizedKeysCommand and AuthorizedKeysCommandUser lines - to (from) `sshd_config` and restarts sshd. -* Adds (or removes) `oslogin` and `cache_oslogin` to (from) `nsswitch.conf`. -* Adds (or removes) the `account` entries to (from) the PAM sshd config. Also - adds (or removes) the `pam_mkhomedir.so` module to automatically create the - home directory for an OS Login user. -* Creates (or deletes) the `/var/google-sudoers.d/` directory, and a file - called `google-oslogin` in `/etc/sudoers.d/` that includes the directory. - #### SELinux Policy The `selinux` directory contains `.te` (type enforcement) and `.fc` (file @@ -167,54 +149,3 @@ * CentOS/RHEL 7 Files for these packages are in the `packaging/` directory. - -#### DEB - -_Note: the `packaging/setup_deb.sh` script performs these steps, but is not -production quality._ - -1. Install build dependencies: - ``` - sudo apt-get -y install make g++ libcurl4-openssl-dev libjson-c-dev libpam-dev - ``` -1. Install deb creation tools: - ``` - sudo apt-get -y install debhelper devscripts build-essential - ``` -1. Create a compressed tar file named - `google-compute-engine-oslogin_M.M.R.orig.tar.gz` using the files in this - directory, excluding the `packaging` directory (where M.M.R is the version - number). -1. In a separate directory, extract the `.orig.tar.gz` file and copy the - `debian` directory into the top level. -1. To build the package, run the command - ``` - debuild -us -uc - ``` - -#### RPM - -_Note: the `packaging/setup_rpm.sh` script performs these steps, but is not -production quality._ - -1. Install build dependencies: - ``` - sudo yum -y install make gcc-c++ libcurl-devel json-c json-c-devel pam-devel policycoreutils-python - ``` -1. Install rpm creation tools: - ``` - sudo yum -y install rpmdevtools - ``` -1. Create a compressed tar file named - `google-compute-engine-oslogin_M.M.R.orig.tar.gz` using the files in this - directory, excluding the `packaging` directory (where M.M.R is the version - number). -1. In a separate location, create a directory called `rpmbuild` and a - subdirectory called `SOURCES`. Copy the `.orig.tar.gz` file into the - `SOURCES` directory. -1. Copy the `SPECS` directory from the `rpmbuild` directory here into the - `rpmbuild` directory you created. -1. To build the package, run the command: - ``` - rpmbuild --define "_topdir /path/to/rpmbuild" -ba /path/to/rpmbuild/SPECS/google-compute-engine-oslogin.spec - ``` diff -Nru google-compute-engine-oslogin-20220714.00/debian/changelog google-compute-engine-oslogin-20231004.00/debian/changelog --- google-compute-engine-oslogin-20220714.00/debian/changelog 2022-11-04 14:07:07.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/debian/changelog 2023-11-08 09:33:39.000000000 +0000 @@ -1,3 +1,16 @@ +google-compute-engine-oslogin (20231004.00-0ubuntu1~22.04.1) jammy; urgency=medium + + * No-change rebuild for Jammy. + + -- Utkarsh Gupta Wed, 08 Nov 2023 11:33:39 +0200 + +google-compute-engine-oslogin (20231004.00-0ubuntu1) noble; urgency=medium + + * New upstream version 20231004.00. (LP: #2043001) + * d/links: Update links w/ respective .so shipped + + -- Utkarsh Gupta Wed, 08 Nov 2023 10:19:01 +0200 + google-compute-engine-oslogin (20220714.00-0ubuntu1~22.04.1) jammy; urgency=medium * No-change rebuild for Jammy. (LP: #1995620) diff -Nru google-compute-engine-oslogin-20220714.00/debian/links google-compute-engine-oslogin-20231004.00/debian/links --- google-compute-engine-oslogin-20220714.00/debian/links 2022-11-03 15:54:04.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/debian/links 2023-11-08 09:33:03.000000000 +0000 @@ -1,2 +1,2 @@ -lib/libnss_cache_oslogin-20220714.00.so lib/libnss_cache_oslogin.so.2 -lib/libnss_oslogin-20220714.00.so lib/libnss_oslogin.so.2 +lib/libnss_cache_oslogin-20231004.00.so lib/libnss_cache_oslogin.so.2 +lib/libnss_oslogin-20231004.00.so lib/libnss_oslogin.so.2 diff -Nru google-compute-engine-oslogin-20220714.00/packaging/debian/control google-compute-engine-oslogin-20231004.00/packaging/debian/control --- google-compute-engine-oslogin-20220714.00/packaging/debian/control 2022-07-12 17:21:31.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/packaging/debian/control 2023-10-04 22:45:15.000000000 +0000 @@ -7,7 +7,7 @@ Package: google-compute-engine-oslogin Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends} +Depends: ${shlibs:Depends}, ${misc:Depends}, google-guest-agent (>= 1:20231003) Description: Google Compute Engine OS Login Contains libraries, applications and configurations for using OS Login on Google Compute Engine Virtual Machine Instances. diff -Nru google-compute-engine-oslogin-20220714.00/packaging/google-compute-engine-oslogin.spec google-compute-engine-oslogin-20231004.00/packaging/google-compute-engine-oslogin.spec --- google-compute-engine-oslogin-20220714.00/packaging/google-compute-engine-oslogin.spec 2022-07-12 17:21:31.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/packaging/google-compute-engine-oslogin.spec 2023-10-04 22:45:15.000000000 +0000 @@ -25,6 +25,7 @@ License: ASL 2.0 Source0: %{name}_%{version}.orig.tar.gz +Requires: google-guest-agent >= 1:20231003 BuildRequires: boost-devel BuildRequires: gcc-c++ @@ -33,7 +34,8 @@ BuildRequires: json-c-devel BuildRequires: pam-devel BuildRequires: policycoreutils -BuildRequires: systemd +BuildRequires: checkpolicy +BuildRequires: systemd Requires: boost-regex Requires: json-c @@ -65,10 +67,10 @@ /%{_lib}/libnss_cache_oslogin-%{version}.so /%{_lib}/libnss_oslogin.so.2 /%{_lib}/libnss_cache_oslogin.so.2 -/%{_lib}/security/pam_oslogin_admin.so /%{_lib}/security/pam_oslogin_login.so /usr/bin/google_authorized_keys /usr/bin/google_authorized_keys_sk +/usr/bin/google_authorized_principals /usr/bin/google_oslogin_nss_cache /usr/share/selinux/packages/oslogin.pp %{_mandir}/man8/nss-oslogin.8.gz diff -Nru google-compute-engine-oslogin-20220714.00/selinux/oslogin.fc google-compute-engine-oslogin-20231004.00/selinux/oslogin.fc --- google-compute-engine-oslogin-20220714.00/selinux/oslogin.fc 2022-07-12 17:21:31.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/selinux/oslogin.fc 2023-10-04 22:45:15.000000000 +0000 @@ -1,2 +1,3 @@ /var/google-sudoers.d(/.*)? system_u:object_r:google_t:s0 /var/google-users.d(/.*)? system_u:object_r:google_t:s0 +/etc/ssh/oslogin_trustedca.pub -p system_u:object_r:sshd_key_t:s0 Binary files /tmp/tmp_5u86dci/SJXvXzzBDH/google-compute-engine-oslogin-20220714.00/selinux/oslogin.pp and /tmp/tmp_5u86dci/E7VuE4OMy_/google-compute-engine-oslogin-20231004.00/selinux/oslogin.pp differ diff -Nru google-compute-engine-oslogin-20220714.00/selinux/oslogin.te google-compute-engine-oslogin-20231004.00/selinux/oslogin.te --- google-compute-engine-oslogin-20220714.00/selinux/oslogin.te 2022-07-12 17:21:31.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/selinux/oslogin.te 2023-10-04 22:45:15.000000000 +0000 @@ -7,9 +7,11 @@ attribute non_security_file_type; type http_port_t; type sshd_t; + type sshd_key_t; class tcp_socket name_connect; class file { create getattr setattr write open unlink }; class dir { search write remove_name add_name }; + class fifo_file { getattr open read }; } #============= types ============== @@ -22,3 +24,4 @@ allow sshd_t google_t:file { create getattr setattr write open unlink }; allow sshd_t google_t:dir { search write remove_name add_name }; allow sshd_t http_port_t:tcp_socket name_connect; +allow sshd_t sshd_key_t:fifo_file { getattr open read }; diff -Nru google-compute-engine-oslogin-20220714.00/src/Makefile google-compute-engine-oslogin-20231004.00/src/Makefile --- google-compute-engine-oslogin-20220714.00/src/Makefile 2022-07-12 17:21:31.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/src/Makefile 2023-10-04 22:45:15.000000000 +0000 @@ -1,7 +1,7 @@ SHELL = /bin/sh TOPDIR = $(realpath ..) -CPPFLAGS = -Iinclude -I/usr/include/json-c +CPPFLAGS = -Iinclude -I/usr/include/json-c -I$(TOPDIR)/third_party/include FLAGS = -fPIC -Wall -g CFLAGS = $(FLAGS) -Wstrict-prototypes CXXFLAGS = $(FLAGS) @@ -28,42 +28,42 @@ NSS_CACHE_OSLOGIN = libnss_cache_oslogin-$(VERSION).so PAM_LOGIN = pam_oslogin_login.so -PAM_ADMIN = pam_oslogin_admin.so -BINARIES = google_oslogin_nss_cache google_authorized_keys google_authorized_keys_sk +BINARIES = google_oslogin_nss_cache google_authorized_keys google_authorized_keys_sk google_authorized_principals -all : $(NSS_OSLOGIN) $(NSS_CACHE_OSLOGIN) $(PAM_LOGIN) $(PAM_ADMIN) $(BINARIES) +.PHONY: all clean install +.DEFAULT_GOAL := all -clean : +all: $(NSS_OSLOGIN) $(NSS_CACHE_OSLOGIN) $(PAM_LOGIN) $(BINARIES) + +clean: rm -f $(BINARIES) find . -type f \( -iname '*.o' -o -iname '*.so' \) -delete -.PHONY : all clean install - # NSS modules. -$(NSS_OSLOGIN) : SONAME = $(NSS_OSLOGIN_SONAME) -$(NSS_OSLOGIN) : nss/nss_oslogin.o oslogin_utils.o +$(NSS_OSLOGIN): SONAME = $(NSS_OSLOGIN_SONAME) +$(NSS_OSLOGIN): nss/nss_oslogin.o oslogin_utils.o $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS) -$(NSS_CACHE_OSLOGIN) : SONAME = $(NSS_CACHE_OSLOGIN_SONAME) -$(NSS_CACHE_OSLOGIN) : nss/nss_cache_oslogin.o nss/compat/getpwent_r.o oslogin_utils.o +$(NSS_CACHE_OSLOGIN): SONAME = $(NSS_CACHE_OSLOGIN_SONAME) +$(NSS_CACHE_OSLOGIN): nss/nss_cache_oslogin.o nss/compat/getpwent_r.o oslogin_utils.o $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS) # PAM modules -$(PAM_LOGIN) : pam/pam_oslogin_login.o oslogin_utils.o - $(CXX) $(CXXFLAGS) $(CPPFLAGS) -shared $^ -o $@ $(PAMLIBS) - -$(PAM_ADMIN) : pam/pam_oslogin_admin.o oslogin_utils.o +$(PAM_LOGIN): pam/pam_oslogin_login.o oslogin_sshca.o oslogin_utils.o include/oslogin_sshca.h $(CXX) $(CXXFLAGS) $(CPPFLAGS) -shared $^ -o $@ $(PAMLIBS) # Utilities. -google_authorized_keys : authorized_keys/authorized_keys.o oslogin_utils.o +google_authorized_principals: authorized_principals/authorized_principals.o oslogin_utils.o oslogin_sshca.o include/oslogin_sshca.h + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $^ -o $@ $(LDLIBS) + +google_authorized_keys: authorized_keys/authorized_keys.o oslogin_utils.o $(CXX) $(CXXFLAGS) $(CPPFLAGS) $^ -o $@ $(LDLIBS) -google_authorized_keys_sk : authorized_keys/authorized_keys_sk.o oslogin_utils.o +google_authorized_keys_sk: authorized_keys/authorized_keys_sk.o oslogin_utils.o $(CXX) $(CXXFLAGS) $(CPPFLAGS) $^ -o $@ $(LDLIBS) google_oslogin_nss_cache: cache_refresh/cache_refresh.o oslogin_utils.o @@ -80,13 +80,13 @@ ln -sf $(NSS_OSLOGIN) $(DESTDIR)$(LIBDIR)/$(NSS_OSLOGIN_SONAME) ln -sf $(NSS_CACHE_OSLOGIN) $(DESTDIR)$(LIBDIR)/$(NSS_CACHE_OSLOGIN_SONAME) # PAM modules - install -m 0644 -t $(DESTDIR)$(PAMDIR) $(PAM_ADMIN) $(PAM_LOGIN) + install -m 0644 -t $(DESTDIR)$(PAMDIR) $(PAM_LOGIN) # Binaries install -m 0755 -t $(DESTDIR)$(BINDIR) $(BINARIES) # Manpages install -m 0644 -t $(DESTDIR)$(MANDIR)/man8 $(TOPDIR)/man/nss-oslogin.8 $(TOPDIR)/man/nss-cache-oslogin.8 - gzip -9 $(DESTDIR)$(MANDIR)/man8/nss-oslogin.8 - gzip -9 $(DESTDIR)$(MANDIR)/man8/nss-cache-oslogin.8 + gzip -9f $(DESTDIR)$(MANDIR)/man8/nss-oslogin.8 + gzip -9f $(DESTDIR)$(MANDIR)/man8/nss-cache-oslogin.8 ln -sf nss-oslogin.8.gz $(DESTDIR)$(MANDIR)/man8/$(NSS_OSLOGIN_SONAME).8.gz ln -sf nss-cache-oslogin.8.gz $(DESTDIR)$(MANDIR)/man8/$(NSS_CACHE_OSLOGIN_SONAME).8.gz ifdef INSTALL_SELINUX diff -Nru google-compute-engine-oslogin-20220714.00/src/authorized_keys/authorized_keys.cc google-compute-engine-oslogin-20231004.00/src/authorized_keys/authorized_keys.cc --- google-compute-engine-oslogin-20220714.00/src/authorized_keys/authorized_keys.cc 2022-07-12 17:21:31.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/src/authorized_keys/authorized_keys.cc 2023-10-04 22:45:15.000000000 +0000 @@ -1,4 +1,4 @@ -// Copyright 2017 Google Inc. All Rights Reserved. +// Copyright 2023 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. @@ -12,10 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include #include -#include -#include #include @@ -23,74 +20,62 @@ using std::cout; using std::endl; -using std::string; -using oslogin_utils::HttpGet; -using oslogin_utils::ParseJsonToSuccess; -using oslogin_utils::ParseJsonToEmail; +using oslogin_utils::AuthOptions; +using oslogin_utils::AuthorizeUser; +using oslogin_utils::CloseSysLog; +using oslogin_utils::FileName; using oslogin_utils::ParseJsonToSshKeys; -using oslogin_utils::UrlEncode; -using oslogin_utils::kMetadataServerUrl; +using oslogin_utils::SetupSysLog; +using oslogin_utils::SysLogErr; -void sigpipe_handler(int signo) { - // exit 0 so SSHD can use what we've already written out. +#define SYSLOG_IDENT "sshd" +#define SUCCESS 0 +#define FAIL 1 + +void signal_handler(int signo) { _Exit(0); } -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { + struct AuthOptions opts; + struct sigaction sig; + char *user_name; + string user_response; + const char *progname = FileName(argv[0]); + + SetupSysLog(SYSLOG_IDENT, progname); + if (argc != 2) { - cout << "usage: authorized_keys [username]" << endl; - return 1; + SysLogErr("usage: %s [username]", progname); + goto fail; } - struct sigaction newact; - newact.sa_handler = sigpipe_handler; - sigemptyset(&newact.sa_mask); - newact.sa_flags = 0; - if (sigaction(SIGPIPE, &newact, NULL) == -1) { - cout << "Unable to add SIGPIPE handler, exiting" << endl; - return 1; + sig = { 0 }; + sig.sa_handler = signal_handler; + sigemptyset(&sig.sa_mask); + + if (sigaction(SIGPIPE, &sig, NULL) == -1) { + SysLogErr("Unable to initialize signal handler. Exiting."); + goto fail; } - std::stringstream url; - url << kMetadataServerUrl << "users?username=" << UrlEncode(argv[1]); - string user_response; - long http_code = 0; - if (!HttpGet(url.str(), &user_response, &http_code) || - user_response.empty() || http_code != 200) { - if (http_code == 404) { - // Return 0 if the user is not an oslogin user. If we returned a failure - // code, we would populate auth.log with useless error messages. - // This exits successfully but prints no keys. - return 0; + user_name = argv[1]; + opts = { 0 }; + + if (AuthorizeUser(user_name, opts, &user_response)) { + // At this point, we've verified the user can log in. Grab the ssh keys from + // the user response. + std::vector ssh_keys = ParseJsonToSshKeys(user_response); + for (size_t i = 0; i < ssh_keys.size(); i++) { + cout << ssh_keys[i] << endl; } - return 1; } - string email; - if (!ParseJsonToEmail(user_response, &email) || email.empty()) { - return 1; - } - // Redundantly verify that this user has permission to log in to this VM. - // Normally the PAM module determines this, but in the off chance a transient - // error causes the PAM module to permit a user without login permissions, - // perform the same check here. If this fails, we can guarantee that we won't - // accidentally allow a user to log in without permissions. - url.str(""); - url << kMetadataServerUrl << "authorize?email=" << UrlEncode(email) - << "&policy=login"; - string auth_response; - if (!HttpGet(url.str(), &auth_response, &http_code) || http_code != 200 || - auth_response.empty()) { - return 1; - } - if (!ParseJsonToSuccess(auth_response)) { - return 1; - } - // At this point, we've verified the user can log in. Grab the ssh keys from - // the user response. - std::vector ssh_keys = ParseJsonToSshKeys(user_response); - for (size_t i = 0; i < ssh_keys.size(); i++) { - cout << ssh_keys[i] << endl; - } - return 0; + + CloseSysLog(); + return SUCCESS; + +fail: + CloseSysLog(); + return FAIL; } diff -Nru google-compute-engine-oslogin-20220714.00/src/authorized_keys/authorized_keys_sk.cc google-compute-engine-oslogin-20231004.00/src/authorized_keys/authorized_keys_sk.cc --- google-compute-engine-oslogin-20220714.00/src/authorized_keys/authorized_keys_sk.cc 2022-07-12 17:21:31.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/src/authorized_keys/authorized_keys_sk.cc 2023-10-04 22:45:15.000000000 +0000 @@ -1,4 +1,4 @@ -// Copyright 2021 Google LLC +// Copyright 2023 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. @@ -12,94 +12,84 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include +#include #include -#include -#include #include -#include #include using std::cout; using std::endl; -using std::string; -using oslogin_utils::HttpGet; -using oslogin_utils::ParseJsonToSuccess; -using oslogin_utils::ParseJsonToEmail; +using oslogin_utils::AuthOptions; +using oslogin_utils::AuthorizeUser; +using oslogin_utils::CloseSysLog; +using oslogin_utils::FileName; using oslogin_utils::ParseJsonToSshKeys; using oslogin_utils::ParseJsonToSshKeysSk; -using oslogin_utils::UrlEncode; -using oslogin_utils::kMetadataServerUrl; +using oslogin_utils::SetupSysLog; +using oslogin_utils::SysLogErr; -void sigpipe_handler(int signo) { - // exit 0 so SSHD can use what we've already written out. +#define SYSLOG_IDENT "sshd" +#define SUCCESS 0 +#define FAIL 1 + +void signal_handler(int signo) { _Exit(0); } int main(int argc, char* argv[]) { + struct AuthOptions opts; + struct sigaction sig; + char *user_name; + string user_response; + bool is_sa = false; + const char *progname = FileName(argv[0]); + + SetupSysLog(SYSLOG_IDENT, progname); + if (argc != 2) { - cout << "usage: authorized_keys_sk [username]" << endl; - return 1; + SysLogErr("usage: %s [username]", progname); + goto fail; } - struct sigaction newact; - newact.sa_handler = sigpipe_handler; - sigemptyset(&newact.sa_mask); - newact.sa_flags = 0; - if (sigaction(SIGPIPE, &newact, NULL) == -1) { - cout << "Unable to add SIGPIPE handler, exiting" << endl; - return 1; - } + sig = { 0 }; + sig.sa_handler = signal_handler; + sigemptyset(&sig.sa_mask); + + if (sigaction(SIGPIPE, &sig, NULL) == -1) { + SysLogErr("Unable to initialize signal handler. Exiting."); + goto fail; + } + + user_name = argv[1]; + is_sa = (strncmp(user_name, "sa_", 3) == 0); + + opts = { 0 }; + opts.security_key = true; + + if (AuthorizeUser(user_name, opts, &user_response)) { + // At this point, we've verified the user can log in. Grab the ssh keys from + // the user response. + std::vector ssh_keys; + if (is_sa) { + // Service accounts should continue to function when SK is enabled. + ssh_keys = ParseJsonToSshKeys(user_response); + } else { + ssh_keys = ParseJsonToSshKeysSk(user_response); + } - bool is_sa = (strncmp(argv[1], "sa_", 3) == 0); - std::stringstream url; - url << kMetadataServerUrl << "users?username=" << UrlEncode(argv[1]) - << "&view=securityKey"; - string user_response; - long http_code = 0; - if (!HttpGet(url.str(), &user_response, &http_code) || - user_response.empty() || http_code != 200) { - if (http_code == 404) { - // Return 0 if the user is not an oslogin user. If we returned a failure - // code, we would populate auth.log with useless error messages. - return 0; + // Print out all available keys. + for (size_t i = 0; i < ssh_keys.size(); i++) { + cout << ssh_keys[i] << endl; } - return 1; } - string email; - if (!ParseJsonToEmail(user_response, &email) || email.empty()) { - return 1; - } - // Redundantly verify that this user has permission to log in to this VM. - // Normally the PAM module determines this, but in the off chance a transient - // error causes the PAM module to permit a user without login permissions, - // perform the same check here. If this fails, we can guarantee that we won't - // accidentally allow a user to log in without permissions. - url.str(""); - url << kMetadataServerUrl << "authorize?email=" << UrlEncode(email) - << "&policy=login"; - string auth_response; - if (!HttpGet(url.str(), &auth_response, &http_code) || http_code != 200 || - auth_response.empty()) { - return 1; - } - if (!ParseJsonToSuccess(auth_response)) { - return 1; - } - // At this point, we've verified the user can log in. Grab the ssh keys from - // the user response. - std::vector ssh_keys; - if (is_sa) { - // Service accounts should continue to function when SK is enabled. - ssh_keys = ParseJsonToSshKeys(user_response); - } else { - ssh_keys = ParseJsonToSshKeysSk(user_response); - } - for (size_t i = 0; i < ssh_keys.size(); i++) { - cout << ssh_keys[i] << endl; - } - return 0; + + CloseSysLog(); + return SUCCESS; + +fail: + CloseSysLog(); + return FAIL; } diff -Nru google-compute-engine-oslogin-20220714.00/src/authorized_principals/authorized_principals.cc google-compute-engine-oslogin-20231004.00/src/authorized_principals/authorized_principals.cc --- google-compute-engine-oslogin-20220714.00/src/authorized_principals/authorized_principals.cc 1970-01-01 00:00:00.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/src/authorized_principals/authorized_principals.cc 2023-10-04 22:45:15.000000000 +0000 @@ -0,0 +1,94 @@ +// Copyright 2023 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 + +#include + +#include +#include + +using std::cout; +using std::endl; + +using oslogin_sshca::FingerPrintFromBlob; + +using oslogin_utils::AuthOptions; +using oslogin_utils::AuthorizeUser; +using oslogin_utils::CloseSysLog; +using oslogin_utils::FileName; +using oslogin_utils::SetupSysLog; +using oslogin_utils::SysLogErr; + +#define SYSLOG_IDENT "sshd" +#define SUCCESS 0 +#define FAIL 1 + +void signal_handler(int signo) { + _Exit(0); +} + +int main(int argc, char* argv[]) { + size_t fp_len; + char *user_name, *cert, *fingerprint; + struct sigaction sig; + struct AuthOptions opts; + string user_response; + const char *progname = FileName(argv[0]); + + fp_len = 0; + opts = { 0 }; + user_name = cert = fingerprint = NULL; + + SetupSysLog(SYSLOG_IDENT, progname); + + if (argc != 3) { + SysLogErr("usage: %s [username] [base64-encoded cert]", progname); + goto fail; + } + + sig = { 0 }; + sig.sa_handler = signal_handler; + sigemptyset(&sig.sa_mask); + + if (sigaction(SIGPIPE, &sig, NULL) == -1) { + SysLogErr("Unable to initialize signal handler. Exiting."); + goto fail; + } + + user_name = argv[1]; + cert = argv[2]; + + fp_len = FingerPrintFromBlob(cert, &fingerprint); + if (fp_len == 0) { + SysLogErr("Could not extract/parse fingerprint from certificate."); + goto fail; + } + + opts.fingerprint = fingerprint; + opts.fp_len = fp_len; + + if (AuthorizeUser(user_name, opts, &user_response)) { + cout << user_name << endl; + } + + free(fingerprint); + CloseSysLog(); + + return SUCCESS; + +fail: + CloseSysLog(); + return FAIL; +} diff -Nru google-compute-engine-oslogin-20220714.00/src/cache_refresh/cache_refresh.cc google-compute-engine-oslogin-20231004.00/src/cache_refresh/cache_refresh.cc --- google-compute-engine-oslogin-20220714.00/src/cache_refresh/cache_refresh.cc 2022-07-12 17:21:31.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/src/cache_refresh/cache_refresh.cc 2023-10-04 22:45:15.000000000 +0000 @@ -168,7 +168,7 @@ } cache_file << "\n"; } - catch (std::ofstream::failure e) { + catch (const std::ofstream::failure &e) { syslog(LOG_ERR, "Exception writing file"); error_code = ENOENT; break; @@ -177,7 +177,7 @@ try { cache_file.close(); } - catch (std::ofstream::failure e) { + catch (const std::ofstream::failure &e) { syslog(LOG_ERR, "Exception closing file"); error_code = ENOENT; } diff -Nru google-compute-engine-oslogin-20220714.00/src/include/oslogin_sshca.h google-compute-engine-oslogin-20231004.00/src/include/oslogin_sshca.h --- google-compute-engine-oslogin-20220714.00/src/include/oslogin_sshca.h 1970-01-01 00:00:00.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/src/include/oslogin_sshca.h 2023-10-04 22:45:15.000000000 +0000 @@ -0,0 +1,50 @@ +// Copyright 2023 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. + +#ifndef _OSLOGIN_SSHCA_H_ +#define _OSLOGIN_SSHCA_H_ 1 + +#include +#include +#include +#include +#include +#include +#include + +#define SKIP_BYTES(b, l, s) \ + { \ + b = b + s; \ + l = l - s; \ + } \ + +#define SKIP_UINT64(b, l) \ + SKIP_BYTES(b, l, 8) \ + +#define SKIP_UINT32(b, l) \ + SKIP_BYTES(b, l, 4) \ + +#define PEEK_U32(p) \ + (((u_int32_t)(((const u_char *)(p))[0]) << 24) | \ + ((u_int32_t)(((const u_char *)(p))[1]) << 16) | \ + ((u_int32_t)(((const u_char *)(p))[2]) << 8) | \ + (u_int32_t)(((const u_char *)(p))[3])) + +namespace oslogin_sshca { +// The public interface - given a blob with a list of certificates we parse each of +// them until we find the first fingerprint. +int FingerPrintFromBlob(const char *blob, char **fingerprint); +} + +#endif diff -Nru google-compute-engine-oslogin-20220714.00/src/include/oslogin_utils.h google-compute-engine-oslogin-20231004.00/src/include/oslogin_utils.h --- google-compute-engine-oslogin-20220714.00/src/include/oslogin_utils.h 2022-07-12 17:21:31.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/src/include/oslogin_utils.h 2023-10-04 22:45:15.000000000 +0000 @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include @@ -220,11 +222,6 @@ // Parses a JSON users response, storing results in a provided string vector. bool ParseJsonToUsers(const string& json, std::vector* users); -// Adds users and associated array of char* to provided buffer and store pointer -// to array in result.gr_mem. -bool AddUsersToGroup(std::vector users, struct group* result, - BufferManager* buf, int* errnop); - // Gets group matching name. bool GetGroupByName(string name, struct group* grp, BufferManager* buf, int* errnop); @@ -281,4 +278,38 @@ // 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: +// <>: <>: <> +// For google_authorized_keys for example, it would look like: +// sshd: google_authorized_keys: <> +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 { + // 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 diff -Nru google-compute-engine-oslogin-20220714.00/src/nss/new_nss_oslogin.c google-compute-engine-oslogin-20231004.00/src/nss/new_nss_oslogin.c --- google-compute-engine-oslogin-20220714.00/src/nss/new_nss_oslogin.c 2022-07-12 17:21:31.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/src/nss/new_nss_oslogin.c 2023-10-04 22:45:15.000000000 +0000 @@ -77,7 +77,8 @@ fprintf (stderr, __VA_ARGS__); \ } while(0) -int parsepasswd(char *str, struct passwd *result, char *buffer, size_t buflen) { +int +parsepasswd(char *str, struct passwd *result, char *buffer, size_t buflen) { int fields[PW_END+1] = {0}; fields[PW_END] = strlen(str)+1; @@ -109,7 +110,8 @@ return 0; } -int parsegroup(char *str, struct group *result, char *buffer, size_t buflen) { +int +parsegroup(char *str, struct group *result, char *buffer, size_t buflen) { int fields[GR_END+1] = {0}; int members[MAX_GR_MEM] = {0}; int i, field, len; @@ -179,7 +181,8 @@ struct Buffer pwbuf; struct Buffer grbuf; -int dial(struct Buffer *const buffer) { +int +dial(struct Buffer *const buffer) { if (buffer->socket != 0) { return 0; } @@ -199,7 +202,8 @@ return 0; } -int recvline(struct Buffer *const buffer) { +int +recvline(struct Buffer *const buffer) { int res = 0; ssize_t recvlen, new_size = 0; fd_set fds; diff -Nru google-compute-engine-oslogin-20220714.00/src/nss/nss_cache_oslogin.c google-compute-engine-oslogin-20231004.00/src/nss/nss_cache_oslogin.c --- google-compute-engine-oslogin-20220714.00/src/nss/nss_cache_oslogin.c 2022-07-12 17:21:31.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/src/nss/nss_cache_oslogin.c 2023-10-04 22:45:15.000000000 +0000 @@ -46,8 +46,8 @@ * so that our caller knows to try again with a bigger buffer. */ -static inline enum nss_status _nss_cache_oslogin_ent_bad_return_code( - int errnoval) { +static inline enum nss_status +_nss_cache_oslogin_ent_bad_return_code(int errnoval) { enum nss_status ret; switch (errnoval) { @@ -70,7 +70,8 @@ // _nss_cache_oslogin_setpwent_locked() // Internal setup routine -static enum nss_status _nss_cache_oslogin_setpwent_locked(void) { +static enum nss_status +_nss_cache_oslogin_setpwent_locked(void) { DEBUG("%s %s\n", "Opening", OSLOGIN_PASSWD_CACHE_PATH); if (p_file) { fclose(p_file); @@ -89,7 +90,8 @@ // Called by NSS to open the passwd file // 'stayopen' parameter is ignored. -enum nss_status _nss_cache_oslogin_setpwent(int stayopen) { +enum nss_status +_nss_cache_oslogin_setpwent(int stayopen) { enum nss_status ret; NSS_CACHE_OSLOGIN_LOCK(); ret = _nss_cache_oslogin_setpwent_locked(); @@ -100,7 +102,8 @@ // _nss_cache_oslogin_endpwent_locked() // Internal close routine -static enum nss_status _nss_cache_oslogin_endpwent_locked(void) { +static enum nss_status +_nss_cache_oslogin_endpwent_locked(void) { DEBUG("Closing %s\n", OSLOGIN_PASSWD_CACHE_PATH); if (p_file) { fclose(p_file); @@ -112,7 +115,8 @@ // _nss_cache_oslogin_endpwent() // Called by NSS to close the passwd file -enum nss_status _nss_cache_oslogin_endpwent(void) { +enum nss_status +_nss_cache_oslogin_endpwent(void) { enum nss_status ret; NSS_CACHE_OSLOGIN_LOCK(); ret = _nss_cache_oslogin_endpwent_locked(); @@ -123,8 +127,9 @@ // _nss_cache_oslogin_getpwent_r_locked() // Called internally to return the next entry from the passwd file -static enum nss_status _nss_cache_oslogin_getpwent_r_locked( - struct passwd *result, char *buffer, size_t buflen, int *errnop) { +static enum nss_status +_nss_cache_oslogin_getpwent_r_locked(struct passwd *result, char *buffer, + size_t buflen, int *errnop) { enum nss_status ret = NSS_STATUS_SUCCESS; if (p_file == NULL) { @@ -150,9 +155,9 @@ // _nss_cache_oslogin_getpwent_r() // Called by NSS to look up next entry in passwd file -enum nss_status _nss_cache_oslogin_getpwent_r(struct passwd *result, - char *buffer, size_t buflen, - int *errnop) { +enum nss_status +_nss_cache_oslogin_getpwent_r(struct passwd *result, + char *buffer, size_t buflen, int *errnop) { enum nss_status ret; NSS_CACHE_OSLOGIN_LOCK(); ret = _nss_cache_oslogin_getpwent_r_locked(result, buffer, buflen, errnop); @@ -163,9 +168,9 @@ // _nss_cache_oslogin_getpwuid_r() // Find a user account by uid -enum nss_status _nss_cache_oslogin_getpwuid_r(uid_t uid, struct passwd *result, - char *buffer, size_t buflen, - int *errnop) { +enum nss_status +_nss_cache_oslogin_getpwuid_r(uid_t uid, struct passwd *result, + char *buffer, size_t buflen, int *errnop) { enum nss_status ret; NSS_CACHE_OSLOGIN_LOCK(); @@ -187,10 +192,9 @@ // _nss_cache_oslogin_getpwnam_r() // Find a user account by name -enum nss_status _nss_cache_oslogin_getpwnam_r(const char *name, - struct passwd *result, - char *buffer, size_t buflen, - int *errnop) { +enum nss_status +_nss_cache_oslogin_getpwnam_r(const char *name, struct passwd *result, + char *buffer, size_t buflen, int *errnop) { enum nss_status ret; NSS_CACHE_OSLOGIN_LOCK(); @@ -211,7 +215,8 @@ // _nss_cache_oslogin_setgrent_locked() // Internal setup routine -static enum nss_status _nss_cache_oslogin_setgrent_locked(void) { +static enum nss_status +_nss_cache_oslogin_setgrent_locked(void) { if (g_file) { fclose(g_file); } @@ -229,7 +234,8 @@ // Called by NSS to open the group file // 'stayopen' parameter is ignored. -enum nss_status _nss_cache_oslogin_setgrent(int stayopen) { +enum nss_status +_nss_cache_oslogin_setgrent(int stayopen) { enum nss_status ret; NSS_CACHE_OSLOGIN_LOCK(); ret = _nss_cache_oslogin_setgrent_locked(); @@ -240,7 +246,8 @@ // _nss_cache_oslogin_endgrent_locked() // Internal close routine -static enum nss_status _nss_cache_oslogin_endgrent_locked(void) { +static enum nss_status +_nss_cache_oslogin_endgrent_locked(void) { DEBUG("%s %s\n", "Closing", OSLOGIN_GROUP_CACHE_PATH); if (g_file) { fclose(g_file); @@ -252,7 +259,8 @@ // _nss_cache_oslogin_endgrent() // Called by NSS to close the group file -enum nss_status _nss_cache_oslogin_endgrent(void) { +enum nss_status +_nss_cache_oslogin_endgrent(void) { enum nss_status ret; NSS_CACHE_OSLOGIN_LOCK(); ret = _nss_cache_oslogin_endgrent_locked(); @@ -263,9 +271,9 @@ // _nss_cache_oslogin_getgrent_r_locked() // Called internally to return the next entry from the group file -static enum nss_status _nss_cache_oslogin_getgrent_r_locked(struct group *result, - char *buffer, size_t buflen, - int *errnop) { +static enum nss_status +_nss_cache_oslogin_getgrent_r_locked(struct group *result, + char *buffer, size_t buflen, int *errnop) { enum nss_status ret = NSS_STATUS_SUCCESS; if (g_file == NULL) { @@ -303,8 +311,9 @@ // _nss_cache_oslogin_getgrent_r() // Called by NSS to look up next entry in group file -enum nss_status _nss_cache_oslogin_getgrent_r(struct group *result, char *buffer, - size_t buflen, int *errnop) { +enum nss_status +_nss_cache_oslogin_getgrent_r(struct group *result, char *buffer, + size_t buflen, int *errnop) { enum nss_status ret; NSS_CACHE_OSLOGIN_LOCK(); ret = _nss_cache_oslogin_getgrent_r_locked(result, buffer, buflen, errnop); @@ -315,9 +324,9 @@ // _nss_cache_oslogin_getgrgid_r() // Find a group by gid -enum nss_status _nss_cache_oslogin_getgrgid_r(gid_t gid, struct group *result, - char *buffer, size_t buflen, - int *errnop) { +enum nss_status +_nss_cache_oslogin_getgrgid_r(gid_t gid, struct group *result, + char *buffer, size_t buflen, int *errnop) { enum nss_status ret; // First check for user whose UID matches requested GID, for self-groups. @@ -367,9 +376,9 @@ // _nss_cache_oslogin_getgrnam_r() // Find a group by name -enum nss_status _nss_cache_oslogin_getgrnam_r(const char *name, struct group *result, - char *buffer, size_t buflen, - int *errnop) { +enum nss_status +_nss_cache_oslogin_getgrnam_r(const char *name, struct group *result, + char *buffer, size_t buflen, int *errnop) { enum nss_status ret; // First check for user whose name matches request, for self-groups. diff -Nru google-compute-engine-oslogin-20220714.00/src/nss/nss_oslogin.cc google-compute-engine-oslogin-20231004.00/src/nss/nss_oslogin.cc --- google-compute-engine-oslogin-20220714.00/src/nss/nss_oslogin.cc 2022-07-12 17:21:31.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/src/nss/nss_oslogin.cc 2023-10-04 22:45:15.000000000 +0000 @@ -50,19 +50,21 @@ extern "C" { // Get a passwd entry by id. -enum nss_status _nss_oslogin_getpwuid_r(uid_t uid, struct passwd *result, - char *buffer, size_t buflen, - int *errnop) { +enum nss_status +_nss_oslogin_getpwuid_r(uid_t uid, struct passwd *result, + char *buffer, size_t buflen, int *errnop) { BufferManager buffer_manager(buffer, buflen); std::stringstream url; url << kMetadataServerUrl << "users?uid=" << uid; + string response; long http_code = 0; - if (!HttpGet(url.str(), &response, &http_code) || http_code != 200 || - response.empty()) { + if (!HttpGet(url.str(), &response, &http_code) || + http_code != 200 || response.empty()) { *errnop = ENOENT; return NSS_STATUS_NOTFOUND; } + if (!ParseJsonToPasswd(response, result, &buffer_manager, errnop)) { if (*errnop == EINVAL) { openlog("nss_oslogin", LOG_PID, LOG_USER); @@ -76,19 +78,21 @@ } // Get a passwd entry by name. -enum nss_status _nss_oslogin_getpwnam_r(const char *name, struct passwd *result, - char *buffer, size_t buflen, - int *errnop) { +enum nss_status +_nss_oslogin_getpwnam_r(const char *name, struct passwd *result, + char *buffer, size_t buflen, int *errnop) { BufferManager buffer_manager(buffer, buflen); std::stringstream url; url << kMetadataServerUrl << "users?username=" << UrlEncode(name); + string response; long http_code = 0; - if (!HttpGet(url.str(), &response, &http_code) || http_code != 200 || - response.empty()) { + if (!HttpGet(url.str(), &response, &http_code) || + http_code != 200 || response.empty()) { *errnop = ENOENT; return NSS_STATUS_NOTFOUND; } + if (!ParseJsonToPasswd(response, result, &buffer_manager, errnop)) { if (*errnop == EINVAL) { openlog("nss_oslogin", LOG_PID, LOG_USER); @@ -103,8 +107,9 @@ // Look for OS Login user with uid matching the requested gid, and craft a // self-group for it. -enum nss_status getselfgrgid(gid_t gid, struct group *grp, - char *buf, size_t buflen, int *errnop) { +enum nss_status +getselfgrgid(gid_t gid, struct group *grp, char *buf, + size_t buflen, int *errnop) { BufferManager buffer_manager(buf, buflen); // Look for a matching user in cache. @@ -142,38 +147,43 @@ // Look for matching user in backend. std::stringstream url; url << kMetadataServerUrl << "users?uid=" << gid; + string response; long http_code = 0; - if (!HttpGet(url.str(), &response, &http_code) || http_code != 200 || - response.empty()) { + if (!HttpGet(url.str(), &response, &http_code) || + http_code != 200 || response.empty()) { return NSS_STATUS_NOTFOUND; } + struct passwd result; - if (!ParseJsonToPasswd(response, &result, &buffer_manager, errnop)) + if (!ParseJsonToPasswd(response, &result, &buffer_manager, errnop)) { return NSS_STATUS_NOTFOUND; + } - if (result.pw_gid != result.pw_uid) + if (result.pw_gid != result.pw_uid) { return NSS_STATUS_NOTFOUND; - + } // Set the group name to the name of the matching user. - if (!buffer_manager.AppendString(result.pw_name, &grp->gr_name, errnop)) + if (!buffer_manager.AppendString(result.pw_name, &grp->gr_name, errnop)) { return *errnop == ERANGE ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; + } grp->gr_gid = result.pw_uid; // Create a list of only the matching user and add to members list. std::vector members; members.push_back(string(result.pw_name)); - if (!AddUsersToGroup(members, grp, &buffer_manager, errnop)) + if (!AddUsersToGroup(members, grp, &buffer_manager, errnop)) { return *errnop == ERANGE ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; - + } return NSS_STATUS_SUCCESS; } // Look for OS Login user with name matching the requested name, and craft a // self-group for it. -enum nss_status getselfgrnam(const char* name, struct group *grp, - char *buf, size_t buflen, int *errnop) { +enum nss_status +getselfgrnam(const char* name, struct group *grp, + char *buf, size_t buflen, int *errnop) { BufferManager buffer_manager(buf, buflen); // Look for a matching user in cache. @@ -207,42 +217,48 @@ // Look for matching user in backend. std::stringstream url; url << kMetadataServerUrl << "users?username=" << UrlEncode(string(name)); + string response; long http_code = 0; - if (!HttpGet(url.str(), &response, &http_code) || http_code != 200 || - response.empty()) { + if (!HttpGet(url.str(), &response, &http_code) || + http_code != 200 || response.empty()) { return NSS_STATUS_NOTFOUND; } + struct passwd result; - if (!ParseJsonToPasswd(response, &result, &buffer_manager, errnop)) + if (!ParseJsonToPasswd(response, &result, &buffer_manager, errnop)) { return NSS_STATUS_NOTFOUND; + } - if (result.pw_gid != result.pw_uid) + if (result.pw_gid != result.pw_uid) { return NSS_STATUS_NOTFOUND; - + } // Set the group name to the name of the matching user. - if (!buffer_manager.AppendString(result.pw_name, &grp->gr_name, errnop)) + if (!buffer_manager.AppendString(result.pw_name, &grp->gr_name, errnop)) { return *errnop == ERANGE ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; + } grp->gr_gid = result.pw_uid; // Create a list of only the matching user and add to members list. std::vector members; members.push_back(string(result.pw_name)); - if (!AddUsersToGroup(members, grp, &buffer_manager, errnop)) + if (!AddUsersToGroup(members, grp, &buffer_manager, errnop)) { return *errnop == ERANGE ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; - + } return NSS_STATUS_SUCCESS; } // _nss_olosing_getgrgid_r() // Get a group entry by id. -enum nss_status _nss_oslogin_getgrgid_r(gid_t gid, struct group *grp, char *buf, - size_t buflen, int *errnop) { +enum nss_status +_nss_oslogin_getgrgid_r(gid_t gid, struct group *grp, char *buf, + size_t buflen, int *errnop) { // If there is no cache file, we will assume there are no groups. - if (access(OSLOGIN_GROUP_CACHE_PATH, R_OK) != 0) + if (access(OSLOGIN_GROUP_CACHE_PATH, R_OK) != 0) { return getselfgrgid(gid, grp, buf, buflen, errnop); + } memset(grp, 0, sizeof(struct group)); BufferManager buffer_manager(buf, buflen); @@ -254,23 +270,26 @@ } std::vector users; - if (!GetUsersForGroup(grp->gr_name, &users, errnop)) + if (!GetUsersForGroup(grp->gr_name, &users, errnop)) { return *errnop == ERANGE ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; + } - if (!users.empty() && !AddUsersToGroup(users, grp, &buffer_manager, errnop)) + if (!users.empty() && !AddUsersToGroup(users, grp, &buffer_manager, errnop)) { return *errnop == ERANGE ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; - + } return NSS_STATUS_SUCCESS; } // _nss_oslogin_getgrnam_r() // Get a group entry by name. -enum nss_status _nss_oslogin_getgrnam_r(const char *name, struct group *grp, - char *buf, size_t buflen, int *errnop) { +enum nss_status +_nss_oslogin_getgrnam_r(const char *name, struct group *grp, + char *buf, size_t buflen, int *errnop) { // If there is no cache file, we will assume there are no groups. - if (access(OSLOGIN_GROUP_CACHE_PATH, R_OK) != 0) + if (access(OSLOGIN_GROUP_CACHE_PATH, R_OK) != 0) { return getselfgrnam(name, grp, buf, buflen, errnop); + } memset(grp, 0, sizeof(struct group)); BufferManager buffer_manager(buf, buflen); @@ -282,26 +301,28 @@ } std::vector users; - if (!GetUsersForGroup(grp->gr_name, &users, errnop)) + if (!GetUsersForGroup(grp->gr_name, &users, errnop)) { return *errnop == ERANGE ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; + } - if (!users.empty() && !AddUsersToGroup(users, grp, &buffer_manager, errnop)) + if (!users.empty() && !AddUsersToGroup(users, grp, &buffer_manager, errnop)) { return *errnop == ERANGE ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; - + } return NSS_STATUS_SUCCESS; } // _nss_cache_oslogin_initgroups_dyn() // Initialize groups for new session. -enum nss_status _nss_oslogin_initgroups_dyn(const char *user, gid_t skipgroup, - long int *start, long int *size, - gid_t **groupsp, long int limit, - int *errnop) { +enum nss_status +_nss_oslogin_initgroups_dyn(const char *user, gid_t skipgroup, long int *start, + long int *size, gid_t **groupsp, + long int limit, int *errnop) { // check if user exists in local passwd DB FILE *p_file = fopen(PASSWD_PATH, "re"); - if (p_file == NULL) + if (p_file == NULL) { return NSS_STATUS_NOTFOUND; + } struct passwd *userp; while ((userp = fgetpwent(p_file)) != NULL) { @@ -386,4 +407,5 @@ (void *)_nss_oslogin_getgrgid_r}, ) NSS_REGISTER_METHODS(methods) + } // extern "C" diff -Nru google-compute-engine-oslogin-20220714.00/src/oslogin_sshca.cc google-compute-engine-oslogin-20231004.00/src/oslogin_sshca.cc --- google-compute-engine-oslogin-20220714.00/src/oslogin_sshca.cc 1970-01-01 00:00:00.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/src/oslogin_sshca.cc 2023-10-04 22:45:15.000000000 +0000 @@ -0,0 +1,294 @@ +// Copyright 2023 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 +#include +#include + +using oslogin_utils::SysLogErr; + +namespace oslogin_sshca { + +typedef struct SSHCertType { + const char *type; + int (*SkipCustomField)(char **buff, size_t *blen); +} SSHCertType; + +static int SkipDSAFields(char **buff, size_t *blen); +static int SkipECDSAFields(char **buff, size_t *blen); +static int SkipED25519Fields(char **buff, size_t *blen); +static int SkipRSAFields(char **buff, size_t *blen); + +static SSHCertType sshca_impl[] = { + {"ecdsa-sha2-nistp256-cert-v01@openssh.com", SkipECDSAFields}, + {"ecdsa-sha2-nistp384-cert-v01@openssh.com", SkipECDSAFields}, + {"ecdsa-sha2-nistp521-cert-v01@openssh.com", SkipECDSAFields}, + {"rsa-sha2-256-cert-v01@openssh.com", SkipRSAFields}, + {"rsa-sha2-512-cert-v01@openssh.com", SkipRSAFields}, + {"ssh-dss-cert-v01@openssh.com", SkipDSAFields}, + {"ssh-ed25519-cert-v01@openssh.com", SkipED25519Fields}, + {"ssh-rsa-cert-v01@openssh.com", SkipRSAFields}, + { }, +}; + +static int GetString(char **buff, size_t *blen, char **ptr, size_t *len_ptr) { + u_int32_t len; + + if (*blen < 4) { + return -1; + } + + len = PEEK_U32(*buff); + if ((*blen - 4) < len) { + return -1; + } + + if (len_ptr != NULL) { + *len_ptr = len; + } + + *buff = *buff + 4; + *blen = *blen - 4; + + if (ptr != NULL) { + *ptr = (char *)malloc(len + 1); + memcpy(*ptr, *buff, len); + ((char *)*ptr)[len] = '\0'; + } + + // Always move the buffer forward. + *buff = *buff + len; + + return 0; +} + +static SSHCertType* GetImplementation(const char *type) { + SSHCertType *iter; + + for (iter = sshca_impl; iter->type != NULL; iter++) { + if (strcasecmp(type, iter->type) == 0) { + return iter; + } + } + + return NULL; +} + +static int SkipRSAFields(char **buff, size_t *blen) { + // Skip e. + if (GetString(buff, blen, NULL, NULL) < 0) { + return -1; + } + + // Skip n. + if (GetString(buff, blen, NULL, NULL) < 0) { + return -1; + } + + return 0; +} + +static int SkipDSAFields(char **buff, size_t *blen) { + // Skip p. + if (GetString(buff, blen, NULL, NULL) < 0) { + return -1; + } + + // Skip q. + if (GetString(buff, blen, NULL, NULL) < 0) { + return -1; + } + + // Skip g. + if (GetString(buff, blen, NULL, NULL) < 0) { + return -1; + } + + // Skip y. + if (GetString(buff, blen, NULL, NULL) < 0) { + return -1; + } + + return 0; +} + +static int SkipED25519Fields(char **buff, size_t *blen) { + // Skip pk. + return GetString(buff, blen, NULL, NULL); +} + +static int SkipECDSAFields(char **buff, size_t *blen) { + // Skip curve. + if (GetString(buff, blen, NULL, NULL) < 0) { + return -1; + } + + // Skip public key. + if (GetString(buff, blen, NULL, NULL) < 0) { + return -1; + } + + return 0; +} + +static int GetExtension(const char *key, size_t k_len, char **exts) { + SSHCertType* impl = NULL; + size_t n_len, t_len, tmp_exts_len, ret = -1; + char *tmp_exts, *tmp_head, *type, *key_b64, *head; + + head = tmp_head = NULL; + + head = key_b64 = (char *)calloc(k_len, sizeof(char)); + if (key_b64 == NULL) { + SysLogErr("Could not allocate b64 buffer."); + goto out; + } + + if ((n_len = b64_pton(key, (u_char *)key_b64, k_len)) < 0) { + SysLogErr("Could encode buffer b64."); + goto out; + } + + // Invalid key (?) + if (n_len <= 4) { + goto out; + } + + if (GetString(&key_b64, &n_len, &type, &t_len) < 0) { + SysLogErr("Could not get cert's type string."); + goto out; + } + + impl = GetImplementation(type); + if (impl == NULL) { + SysLogErr("Invalid cert type: %s.", type); + goto out; + } + + // Skip nonce for all types of certificates. + if (GetString(&key_b64, &n_len, NULL, NULL) < 0) { + SysLogErr("Failed to skip cert's \"nonce\" field."); + goto out; + } + + // Skip type specific fields. + if (impl->SkipCustomField(&key_b64, &n_len) < 0) { + SysLogErr("Failed to skip cert's custom/specific fields."); + goto out; + } + + // Skip serial. + SKIP_UINT64(key_b64, n_len); + + // Skip type. + SKIP_UINT32(key_b64, n_len); + + // Skip key id. + if (GetString(&key_b64, &n_len, NULL, NULL) < 0) { + SysLogErr("Failed to skip cert's \"key id\" field."); + goto out; + } + + // Skip valid principals. + if (GetString(&key_b64, &n_len, NULL, NULL) < 0) { + SysLogErr("Failed to skip cert's \"valid principals\" field."); + goto out; + } + + // Skip valid after. + SKIP_UINT64(key_b64, n_len); + + // Skip valid before. + SKIP_UINT64(key_b64, n_len); + + // Skip critical options. + if (GetString(&key_b64, &n_len, NULL, NULL) < 0) { + SysLogErr("Failed to skip cert's \"critical options\" field."); + goto out; + } + + // Get extensions buffer. + if (GetString(&key_b64, &n_len, &tmp_exts, &tmp_exts_len) < 0) { + SysLogErr("Failed to get cert's \"extensions\" field."); + goto out; + } + + // The field extensions is a self described/sized buffer. + tmp_head = tmp_exts; + if (GetString(&tmp_exts, &tmp_exts_len, exts, &ret) < 0) { + SysLogErr("Failed to read Google's extension."); + goto out; + } + +out: + free(tmp_head); + free(type); + free(head); + + return ret; +} + +static size_t ExtractFingerPrint(const char *extension, char **out) { + int i = 0; + + if (extension == NULL || strstr(extension, "fingerprint@google.com=") == NULL) { + return 0; + } + + for (i = 0; extension[i] != '\0'; i++) { + if (extension[i] == '=') { + *out = strdup(extension + i + 1); + } + } + + return i; +} + +static int GetByoidFingerPrint(const char *blob, char **fingerprint) { + size_t f_len, exts_len = -1; + char *exts = NULL; + + exts_len = GetExtension(blob, strlen(blob), &exts); + if (exts_len < 0) { + SysLogErr("Could not parse/extract extension from SSH CA cert."); + goto out; + } + + f_len = ExtractFingerPrint(exts, fingerprint); + if (f_len == 0) { + SysLogErr("Could not parse/extract fingerprint from SSH CA cert's extension."); + goto out; + } + +out: + free(exts); + + return f_len; +} + +int FingerPrintFromBlob(const char *blob, char **fingerprint) { + if (blob == NULL || strlen(blob) == 0) { + SysLogErr("Could not parse/extract fingerprint from SSH CA cert's extension: \"blob\" is empty."); + return 0; + } + + if (fingerprint == NULL) { + SysLogErr("Could not parse/extract fingerprint from SSH CA cert's extension: \"fingerprint\" is NULL."); + return 0; + } + + return GetByoidFingerPrint(blob, fingerprint); +} + +} diff -Nru google-compute-engine-oslogin-20220714.00/src/oslogin_utils.cc google-compute-engine-oslogin-20231004.00/src/oslogin_utils.cc --- google-compute-engine-oslogin-20220714.00/src/oslogin_utils.cc 2022-07-12 17:21:31.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/src/oslogin_utils.cc 2023-10-04 22:45:15.000000000 +0000 @@ -17,15 +17,19 @@ #include #include #include -#include #include #include #include #include +#include +#include #include +#include #include #include +#include + #include #if defined(__clang__) || __GNUC__ > 4 || \ @@ -47,10 +51,72 @@ const int kMaxRetries = 1; // Regex for validating user names. -const char kUserNameRegex[] = "^[a-zA-Z0-9._][a-zA-Z0-9._-]{0,31}$"; +static const char kUserNameRegex[] = "^[a-zA-Z0-9._][a-zA-Z0-9._-]{0,31}$"; +static const char kSudoersDir[] = "/var/google-sudoers.d/"; +static const char kUsersDir[] = "/var/google-users.d/"; namespace oslogin_utils { +// SysLog wraps syslog operations. +class SysLog { + private: + // app is the application name or the application prefix. + const char *app; + public: + // Prints out an error to syslog. + void Error(const char *fmt, va_list args); + + // Closes the file descriptor being used to write to sys logger. + void Close(); + + // Creates a SysLog instance specifying the ident. Additionally + // it carries an app identifier, so the syslog entries will look like: + // <>: <>: <> + // For google_authorized_keys for example, it would look like: + // sshd: google_authorized_keys: <> + SysLog(const char *ident, const char *app); +}; + +static SysLog *logger = NULL; + +// ----------------- SysLog ------------------------- +SysLog::SysLog(const char *ident, const char *app) { + openlog(ident, LOG_PID|LOG_PERROR, LOG_DAEMON); + this->app = app; +} + +void SysLog::Error(const char *fmt, va_list args) { + std::stringstream new_fmt; + new_fmt << this->app << ": " << fmt; + vsyslog(LOG_ERR, new_fmt.str().c_str(), args); +} + +void SysLog::Close() { + closelog(); +} + +void SetupSysLog(const char *ident, const char *app) { + if (ident != NULL && logger == NULL) { + logger = new SysLog(ident, app); + } +} + +void SysLogErr(const char *fmt, ...) { + if (logger != NULL) { + va_list args; + va_start(args, fmt); + logger->Error(fmt, args); + va_end(args); + } +} + +void CloseSysLog() { + if (logger != NULL) { + logger->Close(); + logger = NULL; + } +} + // ----------------- Buffer Manager ----------------- BufferManager::BufferManager(char* buf, size_t buflen) @@ -91,6 +157,7 @@ : cache_size_(cache_size), entry_cache_(cache_size), page_token_(""), + index_(0), on_last_page_(false) {} void NssCache::Reset() { @@ -1084,18 +1151,27 @@ return true; } -bool GetUser(const string& username, string* response) { +bool MDSGetUser(const string& username, bool security_key, string* response) { std::stringstream url; url << kMetadataServerUrl << "users?username=" << UrlEncode(username); + if (security_key) { + url << "&view=securityKey"; + } + long http_code = 0; if (!HttpGet(url.str(), response, &http_code) || response->empty() || http_code != 200) { return false; } + return true; } +bool GetUser(const string& username, string* response) { + return MDSGetUser(username, false, response); +} + bool StartSession(const string& email, string* response) { bool ret = true; json_object* jobj = NULL; @@ -1170,4 +1246,146 @@ return ret; } + +static bool ApplyPolicy(const char *user_name, string email, const char *policy, struct AuthOptions opts) { + std::stringstream url; + url << kMetadataServerUrl << "authorize?email=" << UrlEncode(email) << "&policy=" << policy; + + // Don't try to add fingerprint parameter to policy call if we don't have it. + if (opts.fp_len > 0) { + url << "&fingerprint=" << opts.fingerprint; + } + + string response; + long http_code = 0; + // Invalid user, just leave from here - the principal will not be allowed/authorized. + if (!HttpGet(url.str(), &response, &http_code)) { + SysLogErr("Failed to validate organization user %s has login permission.", user_name); + return false; + } + + if (http_code != 200) { + SysLogErr("Failed to validate organization user %s has login permission, " + "got HTTP response code: %lu", user_name, http_code); + return false; + } + + if (!ParseJsonToSuccess(response)) { + SysLogErr("Organization user %s does not have login permission.", user_name); + return false; + } + + return true; +} + +static bool FileExists(const char *file_path) { + struct stat buff; + return !stat(file_path, &buff); +} + +static bool CreateGoogleUserFile(string users_filename) { + std::ofstream users_file; + + users_file.open(users_filename.c_str()); + + if (!users_file.is_open()) { + // If we can't open the file (meaning we can't create it) we should report failure. + return false; + } + + // We are only creating the file so we could just close it here. + users_file.close(); + + chown(users_filename.c_str(), 0, 0); + chmod(users_filename.c_str(), S_IRUSR | S_IWUSR | S_IRGRP); + return true; +} + +static bool CreateGoogleSudoersFile(string sudoers_filename, const char *user_name) { + std::ofstream sudoers_file; + + sudoers_file.open(sudoers_filename.c_str()); + + if (!sudoers_file.is_open()) { + // If we can't open the file (meaning we can't create it) we should report failure. + return false; + } + + sudoers_file << user_name << " ALL=(ALL) NOPASSWD: ALL\n"; + sudoers_file.close(); + + chown(sudoers_filename.c_str(), 0, 0); + chmod(sudoers_filename.c_str(), S_IRUSR | S_IRGRP); + return true; +} + +bool AuthorizeUser(const char *user_name, struct AuthOptions opts, string *user_response) { + bool users_file_exists, sudoers_exists; + string email, users_filename, sudoers_filename; + + users_file_exists = sudoers_exists = false; + + if (!ValidateUserName(user_name)) { + return false; + } + + // Call MDS "users?username=" endpoint. + if (!MDSGetUser(user_name, opts.security_key, user_response)) { + return false; + } + + if (!ParseJsonToEmail(*user_response, &email) || email.empty()) { + return false; + } + + users_filename = kUsersDir; + users_filename.append(user_name); + users_file_exists = FileExists(users_filename.c_str()); + + if (!ApplyPolicy(user_name, email, "login", opts)) { + // Couldn't apply "login" policy for user in question, log it and deny. + SysLogErr("Could not grant access to organization user: %s.", user_name); + if (users_file_exists) { + remove(users_filename.c_str()); + } + return false; + } + + if (!users_file_exists && !CreateGoogleUserFile(users_filename)) { + // If we can't create users file we can't grant access, log it and deny. + SysLogErr("Failed to create user's file."); + return false; + } + + sudoers_filename = kSudoersDir; + sudoers_filename.append(user_name); + sudoers_exists = FileExists(sudoers_filename.c_str()); + + if (ApplyPolicy(user_name, email, "adminLogin", opts)) { + // Best effort creating sudoers file, if we fail log it and grant access. + if (!sudoers_exists && !CreateGoogleSudoersFile(sudoers_filename, user_name)) { + SysLogErr("Could not grant sudo permissions to organization user %s." + " Sudoers file %s is not writable.", user_name, sudoers_filename.c_str()); + } + } else { + remove(sudoers_filename.c_str()); + } + + return true; +} + +const char *FileName(const char *file_path) { + int res_start = 0; + for (int i = 0; file_path[i] != '\0'; i++) { + if (file_path[i] == '/') { + res_start = i; + } + } + + if (res_start > 0) { + return file_path + res_start + 1; + } + + return file_path; +} } // namespace oslogin_utils diff -Nru google-compute-engine-oslogin-20220714.00/src/pam/pam_oslogin_admin.cc google-compute-engine-oslogin-20231004.00/src/pam/pam_oslogin_admin.cc --- google-compute-engine-oslogin-20220714.00/src/pam/pam_oslogin_admin.cc 2022-07-12 17:21:31.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/src/pam/pam_oslogin_admin.cc 1970-01-01 00:00:00.000000000 +0000 @@ -1,100 +0,0 @@ -// 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. - -#define PAM_SM_ACCOUNT -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -using std::string; - -using oslogin_utils::HttpGet; -using oslogin_utils::GetUser; -using oslogin_utils::kMetadataServerUrl; -using oslogin_utils::ParseJsonToKey; -using oslogin_utils::ParseJsonToEmail; -using oslogin_utils::ParseJsonToSuccess; -using oslogin_utils::UrlEncode; -using oslogin_utils::ValidateUserName; - -static const char kSudoersDir[] = "/var/google-sudoers.d/"; - -extern "C" { - -PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, - const char **argv) { - // The return value for this module should generally be ignored. By default we - // will return PAM_SUCCESS. - int pam_result = PAM_SUCCESS; - const char *user_name; - if ((pam_result = pam_get_user(pamh, &user_name, NULL)) != PAM_SUCCESS) { - PAM_SYSLOG(pamh, LOG_INFO, "Could not get pam user."); - return pam_result; - } - - if (!ValidateUserName(user_name)) { - // If the user name is not a valid oslogin user, don't bother continuing. - return PAM_SUCCESS; - } - - string response; - if (!GetUser(user_name, &response)) { - return PAM_SUCCESS; - } - - string email; - if (!ParseJsonToEmail(response, &email) || email.empty()) { - return PAM_SUCCESS; - } - - std::stringstream url; - url << kMetadataServerUrl << "authorize?email=" << UrlEncode(email) - << "&policy=adminLogin"; - - string filename = kSudoersDir; - filename.append(user_name); - struct stat buffer; - bool file_exists = !stat(filename.c_str(), &buffer); - long http_code; - if (HttpGet(url.str(), &response, &http_code) && http_code == 200 && - ParseJsonToSuccess(response)) { - if (!file_exists) { - PAM_SYSLOG(pamh, LOG_INFO, - "Granting sudo permissions to organization user %s.", - user_name); - std::ofstream sudoers_file; - sudoers_file.open(filename.c_str()); - sudoers_file << user_name << " ALL=(ALL) NOPASSWD: ALL" - << "\n"; - sudoers_file.close(); - chown(filename.c_str(), 0, 0); - chmod(filename.c_str(), S_IRUSR | S_IRGRP); - } - } else if (file_exists) { - remove(filename.c_str()); - } - return pam_result; -} -} diff -Nru google-compute-engine-oslogin-20220714.00/src/pam/pam_oslogin_login.cc google-compute-engine-oslogin-20231004.00/src/pam/pam_oslogin_login.cc --- google-compute-engine-oslogin-20220714.00/src/pam/pam_oslogin_login.cc 2022-07-12 17:21:31.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/src/pam/pam_oslogin_login.cc 2023-10-04 22:45:15.000000000 +0000 @@ -1,4 +1,4 @@ -// Copyright 2020 Google Inc. All Rights Reserved. +// Copyright 2023 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. @@ -13,17 +13,10 @@ // limitations under the License. #define PAM_SM_ACCOUNT -#include #include -#include -#include #include -#include -#include -#include #include -#include #include #include @@ -31,100 +24,24 @@ using oslogin_utils::ContinueSession; using oslogin_utils::GetUser; -using oslogin_utils::HttpGet; -using oslogin_utils::HttpPost; -using oslogin_utils::kMetadataServerUrl; using oslogin_utils::ParseJsonToChallenges; using oslogin_utils::ParseJsonToKey; using oslogin_utils::ParseJsonToEmail; -using oslogin_utils::ParseJsonToSuccess; using oslogin_utils::StartSession; -using oslogin_utils::UrlEncode; using oslogin_utils::ValidateUserName; -static const char kUsersDir[] = "/var/google-users.d/"; - extern "C" { -PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, - const char **argv) { - const char *user_name; - if (pam_get_user(pamh, &user_name, NULL) != PAM_SUCCESS) { - PAM_SYSLOG(pamh, LOG_INFO, "Could not get pam user."); - return PAM_AUTH_ERR; - } - - if (!ValidateUserName(user_name)) { - // Not a valid OS Login username. - return PAM_IGNORE; - } - - std::string users_filename = kUsersDir; - users_filename.append(user_name); - struct stat buffer; - bool file_exists = !stat(users_filename.c_str(), &buffer); - - std::string str_user_name(user_name); - std::stringstream url; - url << kMetadataServerUrl << "users?username=" << UrlEncode(str_user_name); - - std::string response; - long http_code = 0; - if (!HttpGet(url.str(), &response, &http_code) || response.empty() || http_code != 200) { - if (http_code == 404) { - // This module is only consulted for OS Login users. - return PAM_IGNORE; - } - - // Check local file for that user as a last resort. - if (file_exists) { - return PAM_PERM_DENIED; - } - - // We can't confirm this is an OS Login user, ignore module. - return PAM_IGNORE; - } - std::string email; - if (!ParseJsonToEmail(response, &email) || email.empty()) { - return PAM_AUTH_ERR; - } - - url.str(""); - url << kMetadataServerUrl << "authorize?email=" << UrlEncode(email) << "&policy=login"; - if (!HttpGet(url.str(), &response, &http_code)) { - PAM_SYSLOG(pamh, LOG_INFO, "Failed to validate organization user %s has login permission.", user_name); - return PAM_PERM_DENIED; - } - if (http_code != 200) { - PAM_SYSLOG(pamh, LOG_INFO, - "Failed to validate organization user %s has login permission, got HTTP response code %d.", - user_name, http_code); - return PAM_PERM_DENIED; - } - if (!ParseJsonToSuccess(response)) { - PAM_SYSLOG(pamh, LOG_INFO, "Organization user %s does not have login permission.", user_name); - if (file_exists) { - remove(users_filename.c_str()); - } - return PAM_PERM_DENIED; - } - - PAM_SYSLOG(pamh, LOG_INFO, "Organization user %s has login permission.", user_name); - if (!file_exists) { - std::ofstream users_file(users_filename.c_str()); - chown(users_filename.c_str(), 0, 0); - chmod(users_filename.c_str(), S_IRUSR | S_IWUSR | S_IRGRP); - } +PAM_EXTERN int +pam_sm_setcred(pam_handle_t* pamh, int flags, int argc, const char** argv) { return PAM_SUCCESS; } -PAM_EXTERN int pam_sm_setcred(pam_handle_t * pamh, int flags, int argc, const char **argv) { - return PAM_SUCCESS; -} +PAM_EXTERN int +pam_sm_authenticate(pam_handle_t* pamh, int flags, int argc, + const char** argv) { + const char *user_name; -PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char **argv) -{ - const char* user_name; if (pam_get_user(pamh, &user_name, NULL) != PAM_SUCCESS) { PAM_SYSLOG(pamh, LOG_INFO, "Could not get pam user."); return PAM_PERM_DENIED; @@ -153,14 +70,16 @@ response = ""; if (!StartSession(email, &response)) { - PAM_SYSLOG(pamh, LOG_ERR, "Bad response from the two-factor start session request: %s", + PAM_SYSLOG(pamh, LOG_ERR, "Bad response from the two-factor start session " + "request: %s", response.empty() ? "empty response" : response.c_str()); return PAM_PERM_DENIED; } std::string status; if (!ParseJsonToKey(response, "status", &status)) { - PAM_SYSLOG(pamh, LOG_ERR, "Failed to parse status from start session response"); + PAM_SYSLOG(pamh, LOG_ERR, "Failed to parse status from start session " + "response"); return PAM_PERM_DENIED; } @@ -175,7 +94,8 @@ std::vector challenges; if (!ParseJsonToChallenges(response, &challenges)) { - PAM_SYSLOG(pamh, LOG_ERR, "Failed to parse challenge values from JSON response"); + PAM_SYSLOG(pamh, LOG_ERR, "Failed to parse challenge values from " + "JSON response"); return PAM_PERM_DENIED; } @@ -191,12 +111,14 @@ std::stringstream prompt; prompt << "Please choose from the available authentication methods: "; for(vector::size_type i = 0; - i != challenges.size(); ++i) + i != challenges.size(); ++i) { prompt << "\n" << i+1 << ": " << user_prompts[challenges[i].type]; + } prompt << "\n\nEnter the number for the authentication method to use: "; char *choice = NULL; - if (pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &choice, "%s", prompt.str().c_str()) != PAM_SUCCESS) { + if (pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &choice, "%s", + prompt.str().c_str()) != PAM_SUCCESS) { pam_error(pamh, "Unable to get user input"); return PAM_PERM_DENIED; } @@ -206,6 +128,7 @@ pam_error(pamh, "Error parsing user input"); return PAM_PERM_DENIED; } + if (size_t(choicei) > challenges.size() || choicei <= 0) { pam_error(pamh, "Invalid option"); return PAM_PERM_DENIED; @@ -218,21 +141,23 @@ if (challenge.status != "READY") { // Call continueSession with the START_ALTERNATE flag. if (!ContinueSession(true, email, "", session_id, challenge, &response)) { - PAM_SYSLOG(pamh, LOG_ERR, "Bad response from two-factor continue session request: %s", + PAM_SYSLOG(pamh, LOG_ERR, "Bad response from two-factor continue session " + "request: %s", response.empty() ? "empty response" : response.c_str()); return PAM_PERM_DENIED; } } - char* user_token = NULL; + char *user_token = NULL; if (challenge.type == INTERNAL_TWO_FACTOR) { - if (pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &user_token, "Enter your security code: ") != PAM_SUCCESS) { + if (pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &user_token, + "Enter your security code: ") != PAM_SUCCESS) { pam_error(pamh, "Unable to get user input"); return PAM_PERM_DENIED; } } else if (challenge.type == SECURITY_KEY_OTP) { if (pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &user_token, - "Enter your security code by visiting g.co/sc: ") != PAM_SUCCESS) { + "Enter your security code by visiting https://g.co/sc: ") != PAM_SUCCESS) { pam_error(pamh, "Unable to get user input"); return PAM_PERM_DENIED; } @@ -262,8 +187,10 @@ return PAM_PERM_DENIED; } - if (!ContinueSession(false, email, user_token, session_id, challenge, &response)) { - PAM_SYSLOG(pamh, LOG_ERR, "Bad response from two-factor continue session request: %s", + if (!ContinueSession(false, email, user_token, session_id, + challenge, &response)) { + PAM_SYSLOG(pamh, LOG_ERR, "Bad response from two-factor continue " + "session request: %s", response.empty() ? "empty response" : response.c_str()); return PAM_PERM_DENIED; } diff -Nru google-compute-engine-oslogin-20220714.00/test/Makefile google-compute-engine-oslogin-20231004.00/test/Makefile --- google-compute-engine-oslogin-20220714.00/test/Makefile 2022-07-12 17:21:31.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/test/Makefile 2023-10-04 22:45:15.000000000 +0000 @@ -6,42 +6,51 @@ TEST_RUNNER = ./test_runner --gtest_output=xml NEW_TEST_RUNNER = ./new_test_runner --gtest_output=xml -CPPFLAGS += -I$(TOPDIR)/src/include -I/usr/include/json-c -I$(GTEST_DIR) -isystem $(GTEST_DIR)/include -CXXFLAGS += -g -Wall -Wextra -std=c++11 +SSHCA_TEST_RUNNER = ./sshca_runner --gtest_output=xml --gtest_filter="SSHCATests.*" +CPPFLAGS += -I$(TOPDIR)/src/include -I$(TOPDIR)/third_party/include -I/usr/include/json-c -I$(GTEST_DIR) -isystem $(GTEST_DIR)/include +CXXFLAGS += -g -Wall -Wextra LDLIBS = -lcurl -ljson-c -lpthread -all : test_runner new_test_runner non_network_tests +.PHONY: all clean alltests ping reset +.PHONY: gtest prowtest non_network_tests network_tests +.DEFAULT_GOAL := all + +all: test_runner new_test_runner non_network_tests clean : - rm -f test_runner *.o + rm -f test_runner new_test_runner test_detail.xml *.o -gtest-all.o : $(GTEST_DIR)/src/gtest-all.cc +gtest-all.o: $(GTEST_DIR)/src/gtest-all.cc $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $^ -test_runner : oslogin_utils_test.o $(TOPDIR)/src/oslogin_utils.o gtest-all.o +test_runner: oslogin_utils_test.o $(TOPDIR)/src/oslogin_utils.o gtest-all.o $(CXX) $(CXXFLAGS) $(CPPFLAGS) $^ -o $@ $(LDLIBS) -new_test_runner : oslogin_test.o gtest-all.o +sshca_runner: oslogin_sshca_test.o $(TOPDIR)/src/oslogin_utils.o $(TOPDIR)/src/oslogin_sshca.o gtest-all.o $(CXX) $(CXXFLAGS) $(CPPFLAGS) $^ -o $@ $(LDLIBS) -new_tests : new_test_runner $(TOPDIR)/src/nss/new_nss_oslogin.c +sshca_tests: sshca_runner + $(SSHCA_TEST_RUNNER) + +new_test_runner: oslogin_test.o gtest-all.o + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $^ -o $@ $(LDLIBS) + +new_tests: new_test_runner $(TOPDIR)/src/nss/new_nss_oslogin.c $(NEW_TEST_RUNNER) ${GTESTARGS} -non_network_tests : test_runner new_test_runner +non_network_tests: test_runner new_test_runner $(TEST_RUNNER) --gtest_filter=*-GetGroupByTest.*:GetUsersForGroupTest.* $(NEW_TEST_RUNNER) --gtest_filter=ParserTest.* -network_tests : test_runner ping reset +network_tests: test_runner ping reset $(TEST_RUNNER) --gtest_filter=GetGroupByTest.*:GetUsersForGroupTest.* # run as $ make tests GTESTARGS="--gtest_filter=GetGroupByTest.*" -alltests : test_runner +alltests: test_runner $(TEST_RUNNER) ${GTESTARGS} -ping : +ping: nc -vzw2 169.254.169.254 80 >/dev/null 2>&1 -reset : +reset: curl -Ss http://169.254.169.254/reset >/dev/null 2>&1 - -.PHONY : all clean alltests ping reset gtest prowtest non_network_tests network_tests diff -Nru google-compute-engine-oslogin-20220714.00/test/oslogin_sshca_test.cc google-compute-engine-oslogin-20231004.00/test/oslogin_sshca_test.cc --- google-compute-engine-oslogin-20220714.00/test/oslogin_sshca_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/test/oslogin_sshca_test.cc 2023-10-04 22:45:15.000000000 +0000 @@ -0,0 +1,358 @@ +// Copyright 2023 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 +#include +#include +#include +#include +#include + +using std::string; +using std::vector; + +using oslogin_sshca::FingerPrintFromBlob; + +#define VALID_ECDSA_SINGLE_EXT "AAAAKGVjZHNhLXNoYTItbmlzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg1yMhf" \ + "NVBe4etWEQNDmtxhsAD+YAb7fl/Bn0Z+GGEE9EAAAAIbmlzdHAyNTYAAABBBJ+nM2cR4B" \ + "FHbmokUIScpTaSkx/F1QS2KfIx6z4wcpUmjzKtbP0KFw12mMUiNHzlNBD0B2RnX54uN+k" \ + "bjYGUdSAAAAAAAAAAAAAAAAEAAAAScGFudGhlb24uc2l0YXIubWlnAAAAFgAAABJwYW50" \ + "aGVvbi5zaXRhci5taWcAAAAAAAAAAP//////////AAAAAAAAAEMAAAA7ZmluZ2VycHJpb" \ + "nRAZ29vZ2xlLmNvbT1iODZkYjRjYS0wOWZkLTQyOWUtYjEyMS1hMTI3OTk2MTQwMzIAAA" \ + "AAAAAAAAAAAZcAAAAHc3NoLXJzYQAAAAMBAAEAAAGBAKgQiEGiszewwIeTPZv1/wwQF7K" \ + "JeStkko6w7tcUXRUFWc1ajBUXjEQAxv64JSC+RvlFn7NTVxwzHb+lnbU9+74xfLKB9pqb" \ + "XwiO8HNr4OhLdqfn6x8alfUwsezJzhdBs86o9B9YTFwX2UMJ0c3rZ/0Do6V3WlckMFiPh" \ + "ZiXyiW3pYve+7kj9EZ/WJMAdxTnLPNF03azy3+siyVOWL2zkL8DVscpVVQ51ln15mwvI4" \ + "/e0BVQbP0rtfGIjVOaUM4PyLAfTwg/GPpXroNefvRaxF1scXIjxVQTgm7EauWUyl0i4A1" \ + "sQVEoXWyxQsgdGd6+BTZ3khJCAVSnLTeIhvP9utGJhSLvuJFZu1S1oA1s1/pvpDE9Nfc1" \ + "QT14pWKGUU05Z8yPuBSwbbPqQZvSBhCsVNC5wN6AEkiIsKRJkcSXZIqGQpY9CUAJi9GxS" \ + "R7ATiSy9GAJypHkHNDmJeBEfxYk7Z7jWBZ39HikmAaUfPYzpfjc0nPxYMkMKKy5wnftia" \ + "pVxwAAAZQAAAAMcnNhLXNoYTItNTEyAAABgA4z6rSDNPKG9ae/C11q0U1CsPscK8tXFuK" \ + "surZNXnfAbNwPp8/7x5pamgq5119PnAacll29U9+L+dYwmU8NsJgd1nnQPXh2hLdGs828" \ + "hMOxSgwj35YSzToSulwmOxG7uOYI056WBc3ZZtcxBqvHThLAo6J2TnURicNMvID42ofqQ" \ + "H2Wgozg1AOyHwCDdkrG304NoSfx76tIf/RjLu7yedhhu1x0hbz4DsLLKlm5vVI8jQvLB7" \ + "iz37LMdvftX+Zqnf2EWDT7GspchbrCStH15GXaMCXwyObbDmKkYLz77XckBAQLjm0C6f2" \ + "+f8UxtWzZDvqVFr/iIpivaRUhpGMYNED43XAKR/8uAMKA9d7mB8lD9wMBmRxbG4qDTM4q" \ + "7Q539h7IPMoTRN82VL0v1KPVW8uWqqSjVFdrr2DshitTALMwXpf4VIxw/XuOV5ALNTCan" \ + "bcetrgglFiujUFlIdxkHMmsIxHM88wEnJAlETd7zl9WR/FgQYn3y2dZz9VKoheJdg== " \ + "pantheon.sitar.mig" \ + +#define VALID_ECDSA_MULTI_EXT "AAAAKGVjZHNhLXNoYTItbmlzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb" \ + "20AAAAgcBZK0OB/KoC8ir+mo+aDJm3e88cmk1/UZ+NMhiWyXMQAAAAIbmlzdHAyNTYAA" \ + "ABBBCK4bF9EA181g2ZHWmuggqjsK53SwQKVzyDNZHDIMcCN117t6dSJYvSAgnlg01PGx" \ + "9HyTz7ffcPf3yUfN21WgRsAAAAAAAAAAAAAAAEAAAAWZmluZ2VycHJpbnRAZ29vZ2xlL" \ + "mNvbQAAABoAAAAWZmluZ2VycHJpbnRAZ29vZ2xlLmNvbQAAAABk5O4EAAAAAGbE0HQAA" \ + "AAAAAAAxQAAADtmaW5nZXJwcmludEBnb29nbGUuY29tPWI4NmRiNGNhLTA5ZmQtNDI5Z" \ + "S1iMTIxLWExMjc5OTYxNDAzMgAAAAAAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAA" \ + "AAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd" \ + "2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAA" \ + "AAAAAAAAGgAAAATZWNkc2Etc2hhMi1uaXN0cDI1NgAAAAhuaXN0cDI1NgAAAEEErH/DI" \ + "zvUUx1Isb5xtFpgt2TgPsB9QfbM7EAGKJ8yZaljZr2blH+XsQjIognAv3FCE3t3zTshl" \ + "8atWl5fzzXa4QAAAGUAAAATZWNkc2Etc2hhMi1uaXN0cDI1NgAAAEoAAAAhAPTeGWrdg" \ + "chbWRO1o6ignVyuwq6tTjz/rSfzkjDZw6BsAAAAIQCSDGI9KQuAxhaVDhD9y1XHm2s+I" \ + "+IddaiA/0hzb4MDtA== fingerprint@google.com" \ + +#define INVALID_ECDSA_NO_FP "AAAAKGVjZHNhLXNoYTItbmlzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgxlbtL" \ + "/mjYXEgsXjl7GZgpvIFncxbfmjPYVewm1sdXo4AAAAIbmlzdHAyNTYAAABBBMYdGLr6M" \ + "102qgBeJ3CanDi0WV1vGif2jMMv1ldtN0+wbDztYdtUu8iop/tN46wFVbfmSzyx/R2YL" \ + "bvQ+z2k/sYAAAAAAAAAAAAAAAEAAAAWZmluZ2VycHJpbnRAZ29vZ2xlLmNvbQAAABoAA" \ + "AAWZmluZ2VycHJpbnRAZ29vZ2xlLmNvbQAAAABk0UUMAAAAAGaxJ2gAAAAAAAAAAAAAA" \ + "AAAAABoAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPoCTDKl0" \ + "jZG3V2P14+PDbYrD0sDBsKwYaZEn85tM6mmGEY1yHg/VI76O27xTqE56PJECph1eKmo4" \ + "YA/ch8wwewAAABkAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAABJAAAAIQCN6KRgSVKYH" \ + "pM3dlil8jDXlpL4U1JSmP3MeHX0OKcpHgAAACAYiWa3KrreEzN+VrnuhwStH70bvH9Qm" \ + "6Va6a0IcMrMkA== fingerprint@google.com" \ + +#define INVALID_ECDSA_NON_CERT "AAAAE2VjZHNhLXNoYTI" \ + "tbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMYdGLr6M102qgBeJ3CanDi0WV1vGif2jMM" \ + "v1ldtN0+wbDztYdtUu8iop/tN46wFVbfmSzyx/R2YLbvQ+z2k/sY= " \ + "fingerprint@google.com" \ + +#define VALID_RSA_SINGLE_EXT "AAAAHHNzaC1yc" \ + "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" \ + "GqmJpNjwsqExxW5E= fingerprint@google.com" \ + +#define VALID_RSA_MULTI_EXT "AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgpv8XuCPuX0/2hATuCuFa1kVXR" \ + "CNzX7gU6T4Q/EVZiMkAAAADAQABAAABAQDPh7YORgzS7V3F5oxVlwTABglvV6cUx32GO7" \ + "I84CxVRnWdW9D4eQoRD+lN8YKcbWN826/G9A9AIyADl6nMpxocgymCCyz4ujapTf/ntaH" \ + "pc7QTNuKDQ3x9ptHVjPSbXx+HVBC0gFgCxRlymAjN8P9Rex+wkJRMPCOIwykO9H5BkDfc" \ + "iZMcPc+BAVvM/A+oREjHVO7yyOEiMXByoiXOg9yd4KM70ypmAOLan4unQRy10Bye6U2fL" \ + "mqkPzfLIQpdExBmU+MEEBum+Kqk3pdppwli/EnueHSkljtJLBBID5bD3xEzNcdi107OoW" \ + "fXBgiTAyewrW7GCYw1V27LpUwg21/lAAAAAAAAAAAAAAABAAAAFmZpbmdlcnByaW50QGd" \ + "vb2dsZS5jb20AAAAaAAAAFmZpbmdlcnByaW50QGdvb2dsZS5jb20AAAAAZOTh1AAAAABm" \ + "xMQ6AAAAAAAAAMUAAAA7ZmluZ2VycHJpbnRAZ29vZ2xlLmNvbT1iODZkYjRjYS0wOWZkL" \ + "TQyOWUtYjEyMS1hMTI3OTk2MTQwMzIAAAAAAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZw" \ + "AAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZ" \ + "vcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAA" \ + "AAAAAAAAAAIXAAAAB3NzaC1yc2EAAAADAQABAAACAQDY+memx1OUatqYIbrKErOTXM1/h" \ + "rqeDmT423gK5ecqmzJt86ZS1Z1WhuqOD4CW9YISZP2VpScV61Cj4OF5MuEi8V7UaaJf6N" \ + "himixleP88rCxCbXWc9MwX7xBnX8spvOPcrof9zs8fKnDJuhRMMf614gfD3C0cPpahtxx" \ + "4n7KytJ14jSKaECUjcpZ+f30WHrZvLY4sJMmMBJhcuMWC6Y1lckMT0t9M0pjRs2ZUOOyc" \ + "R5wTxybr7rFxzQhHiSpfXeVwErz8b+5IxvvlqUCawTmVmntcP9atobNZCIRt28K6Fyw7A" \ + "AjoD0jP3nLoEQuk2As4erfmuabBZK4HwxoaWVSbsV8T7RYq/JiDdvP6x+BbEhgmrnBRUA" \ + "dPTRy2fEFxgIKbKhg8tm5M9GO8k/VeVykeOmcL88Da2swXuCcp1wAQjrrn81jyunsVlLG" \ + "Kzeco3qrSn/6nwtcNOu2I8JNwk1GKvV7KTYEL/xNQSQ2Pk6r1HlPlyq/eo3HuFE/NxO9u" \ + "iXLV3bapMSt3KsvCkTpLW1eJLg9bytd2aVpZW7s4uuR1mTZfgDPM75zXubkgqA2RVQ7Tl" \ + "76MzBW9LL1f/B7lMxdJYQF1WqqSJNVcRLS5L0zpuS9Z48piYv8v2ioJGCFae+CnwmNYw+" \ + "wPAd0MXp1X6808ceRvmqADSbU4zxH00BUIdwAAAhQAAAAMcnNhLXNoYTItNTEyAAACAF6" \ + "7EZPDjyBO6+Zv88KnNyTFkQ5+wbS2DzD9myW/cSGxEvKX/Ccznzi8ROesNzjv4vOJja3Z" \ + "2UIm4LjmzVXrTJsu0XFQ8NnN8Bk1GedqxLgYUfEgTkVh2Wj778Cw278NTQFRqwdkYrK3q" \ + "DksHGrp8xoXNb7kf8Kws1R4GS8ue0mW5QFgQRd2WLRckYh5S9cnDMbw4wGrZFFu75RJUA" \ + "lozlB7sDCcMJRtJ5VmU8PgzyZpsRm2GnNCLqbnH/QbH3wPnHgbtaZqGU5vU2uRkwML+P8" \ + "mn8fbePqOw4sC5sGvxOZ3Zr6S22WygRaoq7iM6w4Yhjg57Ga0RRsT8KbAmFyZlnghroS8" \ + "9R84iVJPDxjSskrpY1oM5pjonvmD/3GeGd6oXl/x9A+df5YBiVxn6KiXgbS90yYXJFpeh" \ + "xE+whj5PeNlL/6qaqf0MesCHT+6Uwo/Hp7DAbRCzEt8KBWr1nt6bLwEzitT4nokTljo70" \ + "ctSlNsmXAOalqatlffQnGF1J5n3HDbPH6zKon82MMAnlha+SGfDQqc1uhMdfbfL7DMhFm" \ + "xLPX5BvoRzQT96EGgWjhlmI7j2e8fghkjsCwaH7HrfSBuXYvw1DPRBaOktIEDPk9tF70B" \ + "WIdoJJX2phxK1km8+78sdCbtVVaTzlGNDflqM++kqmNHhZFtoWRYeHKYHRFo " \ + "fingerprint@google.com" \ + +#define INVALID_RSA_NO_FP "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 " \ + "fingerprint@google.com" \ + +#define INVALID_RSA_NON_CERT "AAAAB3NzaC1yc2EAAAADAQABAAABAQCU/m" \ + "ydd9mSwlSDv4T3OiL5IHrvSuXpWFvCEDmVyLxBHz1FCwjnk3G5xSt9nGtUyL0KpGt0dyv" \ + "LU07JGB33cbVnVe1z3373FNKxF8LdwDTEZG6xijXuOi4xfk47arlpk9Pw14qcnVu9on4R" \ + "m4cSmm5PkyIwTfJsKvOl8oOgZ0HZG7pzYEt+9wUoeGzUE0rsAreNFVB7ZBqHp2ZtdIe5d" \ + "dbarKAl1JKZuz8EbUmdjBYXsMYLHd0gUd+rvHyaw7p3iJyESaJcqUOQHrecpkLqWiN+TU" \ + "NZPchE/T19LSP/fQbPCGmqc+mC6YodSEbLkO6JmOaW+knTEc9D6xdozx6Oa4vR " \ + "fingerprint@google.com" \ + +#define VALID_DSA_SINGLE_EXT "AAAAHHNzaC1kc3MtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgH400e9SzsvaN8OkKvH26sXEJtU/BVc2IBG" \ + "fdZDHk508AAACBAO9UdOmq7Z0qy86mwsDf07TmXQe7X0TLKbyFSsd2b+jTCzpXy9rBhgg" \ + "oJlzYzxSQgtR4JaSTauZMiQQViN3cKvHuGfAXIOIMtMHVupNy6WSkcixGrvw6Y0Yr90+e" \ + "8PXcFw6jwQbFZX4v9zlUuIl067rCrxp1jnhBjxvBZEmpR/ezAAAAFQCO10V2wYXJ7cSo4" \ + "eEgHB1BnOxbzwAAAIEAzbdt5bgzV164ljY6dimHUUnKUnYEq7VY3gJZSN3YGwMHnIYw94" \ + "gtNxkhP09SkPn+llAH/NTKq2beu9GizybqIc9Gtfh3AGqYLyWhePZumcUYYShMc7eNUfN" \ + "RDp1QtWnX/A/HvVsNEdqHW3R9cq5miEWgoUFHOHhLZtUOk1wbJe4AAACAITYKkJACQAoh" \ + "b9q1Ehxea8qoYaM2ctI3JCN59bbcgP1Tngaq2TcJOup0+AVN5P7ILQGh3s9xngdcQU9RJ" \ + "XlUh6yHpW0BwkAOAKjX7ASjx4rKOkN0PeT2KtyGWqLcnbFRSQGNQOs+vv3TIUofZosXKT" \ + "A2EtmjpKcIbfu3lF+J50gAAAAAAAAAAAAAAAEAAAAWZmluZ2VycHJpbnRAZ29vZ2xlLmN" \ + "vbQAAABoAAAAWZmluZ2VycHJpbnRAZ29vZ2xlLmNvbQAAAABk0SkoAAAAAGaxC5IAAAAA" \ + "AAAAQwAAADtmaW5nZXJwcmludEBnb29nbGUuY29tPWI4NmRiNGNhLTA5ZmQtNDI5ZS1iM" \ + "TIxLWExMjc5OTYxNDAzMgAAAAAAAAAAAAABsgAAAAdzc2gtZHNzAAAAgQC1WfAI4qlV3J" \ + "jY+tsHhQEVJgMVPzrNa94pvSDRc9FYexYHaky6e0zroP8LmxTEZyOzfT9H22lqmIPRHXp" \ + "dEB5ge7FAO7/QsavsAUQHnpiyaqng4ojgPTQJzY0tuYUkV9a265gZJpqdY0wNLcSJtDFk" \ + "WtixpTSswF1gGs5maq5ljQAAABUAlYIrD8mImqdRNg7FizxOscWV8WUAAACBAJE9UuRok" \ + "fqK1ZodxfX0Me1NgS+4rpH9iWqHudpQiR134OUT3w29dtTDKdDjesuOyEDGi17Z5Honsv" \ + "yktNVzel+F8q6/24NI4VSBnRXi8TkXvK6BjRFJsnVJRMiF8zErd3ihWDmsEXcMJ0X/uQw" \ + "tBKREhGri3xz1bVVH+Iwb8F5SAAAAgFtqHHk0TP8wLmggYh+i9FDhN+99yt7FxDAGg4di" \ + "JDkpmo8MUaXmxibghK0c15Tta9hoOUqtArpYdBm0WyfEM/5Us2qVVcJp4Cjrw9OJ3lEgj" \ + "+OSizEVlMEujCFT/j032c5Y4O3ScCEUDrFjlutMtlUfvPaDGX7yH0mYONAb6p2FAAAANw" \ + "AAAAdzc2gtZHNzAAAAKH5faM5YTlMn+h2cf99PJ8rjvqQUJoh5yi3a4pkGcr5MJs53Wfi" \ + "DPaA= fingerprint@google.com" \ + +#define VALID_DSA_MULTI_EXT "AAAAHHNzaC1kc3MtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg4F67aKUUtM8mWvtHxr2AjRcDB" \ + "jkmICwZRUOx4JaDVYEAAACBAKbdH1vmX/ZCVY1v41hXxEroqQpfOGR+G/0gtuscO5rU+c" \ + "9T4qq5lm3E+SwFfCCqC4x6+zDomsJvptMJU0r1oxMuXDo0PRtr4qMMKw0FwZ29D+9zITb" \ + "FvaRUc4+FQ5JvxCUBEKQxzetsTyIsirM4vWW6oKMGACAvgs3qu+CrPKtnAAAAFQDWlrhr" \ + "iKONlBabChlcap+cmeMzvQAAAIEAnlrkClDOBZ0Cx+cQF201G3Bq9eThHYo+sxydojtIW" \ + "SYAJFYLvQjF0r/34Wxj5sBgxcGhe8yp3Y+ZggB3vGZ6UjzCy6F6zkfgyl+KzYfV42uRrW" \ + "+7dn7VChySMM2OcgTnN69QMTkym8Pv00qF+a0XD1mH9uK0l1q0eZtndj59rfUAAACARtR" \ + "gCOBB7JoU1Br38bo6VNww26oRV4BkVEQN9l3M+6sxG0IL8brBuCh1JLyQVLMcXNj+K2pQ" \ + "PH8JDKdOrbP/xarcRY+fhRN5IvP5n/fNOJp3oXsvjiOeH1z4u1Ra7e0DAoJEOofKbr/sg" \ + "QfCNsB4gP4u62ck27w2pRXNdxJKyrkAAAAAAAAAAAAAAAEAAAAWZmluZ2VycHJpbnRAZ2" \ + "9vZ2xlLmNvbQAAABoAAAAWZmluZ2VycHJpbnRAZ29vZ2xlLmNvbQAAAABk5Nv4AAAAAGb" \ + "Evl8AAAAAAAAAxQAAADtmaW5nZXJwcmludEBnb29nbGUuY29tPWI4NmRiNGNhLTA5ZmQt" \ + "NDI5ZS1iMTIxLWExMjc5OTYxNDAzMgAAAAAAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nA" \ + "AAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm" \ + "9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAA" \ + "AAAAAAAAAAbEAAAAHc3NoLWRzcwAAAIEAuoOoF4etzwXHXkc4b1Wx15adJkLBzbRARAuc" \ + "A360XxdxzO+Gt5A/OLV7eE8jxVaz0sC9CE1ikpAp/u0ZL+tVZyA0X2KMAJetgFxVZueyI" \ + "wHY1IKOzJibJ4OP8re3MiYYoxdAd2fK4n9x/IvjIIXy8GfEsiBQXNEBDcMKTCGgJC0AAA" \ + "AVAPhsO/SR/pV7M52uwsfIbnTshxC/AAAAgCEG5HUjilYhxoWKAXhdsnEHKGzv9zDTkBQ" \ + "9c5zrG/ZegmJiFrpmwL2ON38Co+BcH88kxDjdyVOkIncldxVd0OpdAGLClhEVeY3g4nWl" \ + "DYPPxkH4GJapMltkYMwa6HaWCRRgNE/aEwcAyMj3lwtCRXtX33tMM+9hjDHUbRNkpv60A" \ + "AAAgB/6hg9VhH/eJLQm3URYl+dXSiBONDkbLzKHUvSaAqmItoDDsW6N/pd5XqrSzLxa1R" \ + "DihDoRNZbZ7uWCjRKfwoPZTKL42OV4WRa//gPDzx55zECZokYg0d5/AbZ3pmf9XYo2Lka" \ + "eA3PlT8Oz/DABW3BKipLrvXhZYAn8PumuUNsdAAAANwAAAAdzc2gtZHNzAAAAKBleCvo9" \ + "QgobHREVlFH0/E84XhTVRfOok7RE4ht2EOiZLG2cfThvWUQ= " \ + "fingerprint@google.com" \ + +#define INVALID_DSA_NO_FP "AAAAHHNzaC1kc3MtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgGrlYnOqQxs/zzfWRcrM7DHrFy653/x7rtOghw" \ + "R/f3HIAAACBALzWA8yWLownZsO4Tuc4DF6EplCJ1SBSEqMYAEhzrnxjHkoOpJ3Ncs+Zn5" \ + "jdcnCamkm6KQ4keXkV0xwLthRgLxhUguc9xANV5k2Vft+axWr+cp+KNiGzDjblTUnWzQD" \ + "5Q/mBpiFKL7EiZski3swpJQs0kGQW2hxbjlr7I0EhM8e1AAAAFQDdVQaUxoK58jpTFdVi" \ + "gI3yzjtK3wAAAIB1Z8nZ4QEaqSLK5+Xm2LAbn32Do0nGtOkPCWYjZzlcfHt1Hb7gCIe5X" \ + "gqPZM3hYWuOisKsk2gxxeVyiX6VBuYpCbINduxw8h/7nMyOTFLr01Y282/eHq20XHPLD2" \ + "2hdY1l44de8EhYrcHRPM7twnFJU0X7og6QNdOnvMXQ+WclWwAAAIAmJmVahDp1Vu22M9t" \ + "7V4yRYP8Wh3ROletzPrY9kpgTR5QtfZ57QPxN3n2r61iLaPWR0cQ12x4LviBVTsFk87oE" \ + "9SAxzcDPwyzbSM3ATIzI6TauIVacVFoUdeAy2rjaLUGYcdD745oudXmyXq1VupLHaJC9k" \ + "ePm8hkeZyf/5F6XBwAAAAAAAAAAAAAAAQAAABZmaW5nZXJwcmludEBnb29nbGUuY29tAA" \ + "AAGgAAABZmaW5nZXJwcmludEBnb29nbGUuY29tAAAAAGTRQFwAAAAAZrEiogAAAAAAAAA" \ + "AAAAAAAAAAbEAAAAHc3NoLWRzcwAAAIEA4FZIvoI2syNIyZ34DibfH4Pm3Rf0iKHUIgLR" \ + "+izM/aP9jDAXRD/c2Tl2cnw4pVJdun2+ByBNkRHQJ+86dMVXhvpIPjoeK4dqJsEBsSj7L" \ + "ohXMJtdn5LyBpiiyZ4jq7uVeWGm1q7Lh6WeIuBVNLgwoE1/z5RtScGhbHBPb8q7RbcAAA" \ + "AVAOHoBqU7wxf09lWcarL6SaOAyWJ5AAAAgGLb9fIGSP50+sfKqxSohCU23B3SYCIf7QI" \ + "1Zjql9FeDY9AfvkzVaiJvA/eoZKwGhG5FbDtA9eyuCfiB5E6VqaShx3Mp3yCKPOaCznrv" \ + "LKJsqMKC7ReU2obugmMELRmbTdZdQCdvvNrVjqvW54aUIzF4zC9ZKeiKtG6MQ7VP/MrRA" \ + "AAAgGk5pXHfmjL8vDZIwtWhxm3gdN5TubyKgW1i/nIMDgLhLqLw4//NY86wkGj84MwniT" \ + "Gf2pB8lGzBPj+ByQIMABe/iMq9uRXLNUFta7PYQKi3UjCoCwv0p88advtwOXRyHu1THxr" \ + "JDMmmDirJnSYW8I7F9gY4UMldYwy9dyNqwfoQAAAANwAAAAdzc2gtZHNzAAAAKAnb/pHN" \ + "+YzrU7BOR7qnGs1qJqWhgFKXETMeHxPzpi4ny9tSNlI6c0g= " \ + "fingerprint@google.com" \ + +#define INVALID_DSA_NON_CERT "AAAAB3NzaC1kc3MAAACBAO9UdOmq7Z0qy8" \ + "6mwsDf07TmXQe7X0TLKbyFSsd2b+jTCzpXy9rBhggoJlzYzxSQgtR4JaSTauZMiQQViN3" \ + "cKvHuGfAXIOIMtMHVupNy6WSkcixGrvw6Y0Yr90+e8PXcFw6jwQbFZX4v9zlUuIl067rC" \ + "rxp1jnhBjxvBZEmpR/ezAAAAFQCO10V2wYXJ7cSo4eEgHB1BnOxbzwAAAIEAzbdt5bgzV" \ + "164ljY6dimHUUnKUnYEq7VY3gJZSN3YGwMHnIYw94gtNxkhP09SkPn+llAH/NTKq2beu9" \ + "GizybqIc9Gtfh3AGqYLyWhePZumcUYYShMc7eNUfNRDp1QtWnX/A/HvVsNEdqHW3R9cq5" \ + "miEWgoUFHOHhLZtUOk1wbJe4AAACAITYKkJACQAohb9q1Ehxea8qoYaM2ctI3JCN59bbc" \ + "gP1Tngaq2TcJOup0+AVN5P7ILQGh3s9xngdcQU9RJXlUh6yHpW0BwkAOAKjX7ASjx4rKO" \ + "kN0PeT2KtyGWqLcnbFRSQGNQOs+vv3TIUofZosXKTA2EtmjpKcIbfu3lF+J50g= " \ + "fingerprint@google.com" \ + +#define VALID_ED25519_SINGLE_EXT "AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIDaErnQWEw/jxPD0JUJsEk" \ + "CtENcE11Zl53QHbxbAgx22AAAAIHs6r2AekiTHmmoJMKxAKtKW4qcGq5Ku1+SJ1NLdZh0" \ + "1AAAAAAAAAAAAAAABAAAAFmZpbmdlcnByaW50QGdvb2dsZS5jb20AAAAaAAAAFmZpbmdl" \ + "cnByaW50QGdvb2dsZS5jb20AAAAAZNEqzAAAAABmsQ0IAAAAAAAAAEMAAAA7ZmluZ2Vyc" \ + "HJpbnRAZ29vZ2xlLmNvbT1iODZkYjRjYS0wOWZkLTQyOWUtYjEyMS1hMTI3OTk2MTQwMz" \ + "IAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgyxEJdP6tUhJY3J/4bgLpzyUojE9" \ + "6YKzE2t/RAx5l32kAAABTAAAAC3NzaC1lZDI1NTE5AAAAQNEBsSEvp5tVMbKUsjIZ0jEa" \ + "Yv0T0U/GZoCiLfVm3pcXV3RA8aze+y/pbjv+MOxjmAb4KbRH31/S34UALsyGwQM= fing" \ + "erprint@google.com" \ + +#define VALID_ED25519_MULTI_EXT "AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIEBlk2f75yvu5" \ + "8QqsykJfRrKxblQi2RmcW2bzj9mhi2YAAAAINYsHqqaS4JdLuAevLnHc7lBu0qv2/Lfx+" \ + "VLRTIIA5wxAAAAAAAAAAAAAAABAAAAFmZpbmdlcnByaW50QGdvb2dsZS5jb20AAAAaAAA" \ + "AFmZpbmdlcnByaW50QGdvb2dsZS5jb20AAAAAZOTuuAAAAABmxND2AAAAAAAAAMUAAAA7" \ + "ZmluZ2VycHJpbnRAZ29vZ2xlLmNvbT1iODZkYjRjYS0wOWZkLTQyOWUtYjEyMS1hMTI3O" \ + "Tk2MTQwMzIAAAAAAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LW" \ + "FnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAA" \ + "ACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3Nz" \ + "aC1lZDI1NTE5AAAAIJD/WK1OEhbe0bG/3ibbjawl0FNHf3nho9hF9D5QcXOPAAAAUwAAA" \ + "Atzc2gtZWQyNTUxOQAAAEANxz8Lv5Ojc0U1SIU5eGoGk8N+LAHS5/OfB3AvLT94raJ8qc" \ + "lB7KvEgKOycsF5xLJOL9+/oe29SeNTq+ubIkIN fingerprint@google.com" \ + +#define INVALID_ED25519_NO_FP "AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIDDgIXa9QLFY7RpSNnWDm3Saq" \ + "YZ5HGcpzHq9hdv64nqXAAAAIKfDRdZjpCb2YVsmhs286hQTH7JFctizNC0W7UQKfruSAA" \ + "AAAAAAAAAAAAABAAAAFmZpbmdlcnByaW50QGdvb2dsZS5jb20AAAAaAAAAFmZpbmdlcnB" \ + "yaW50QGdvb2dsZS5jb20AAAAAZNFCeAAAAABmsSTsAAAAAAAAAAAAAAAAAAAAMwAAAAtz" \ + "c2gtZWQyNTUxOQAAACBTEPiuWCgwX9JhFzMNLex4d9uRtdWfUg0OCAdH6nVbsAAAAFMAA" \ + "AALc3NoLWVkMjU1MTkAAABAt2CPRZos3Lna+44LwI6ON8rRktxAqz1S4nUf+IwrG83Wbv" \ + "nEvvZ2plHLTAU7GP2ZMedVKoXB9KXB2vNBVjt9Cg== fingerprint@google.com" \ + +#define INVALID_ED25519_NON_CERT "AAAAC3NzaC1lZDI1NTE5AAAAIH" \ + "s6r2AekiTHmmoJMKxAKtKW4qcGq5Ku1+SJ1NLdZh01 fingerprint@google.com" \ + +TEST(SSHCATests, TestValidSingleExtCert) { + struct { + const char *key; + } *iter, tests[] = { + {VALID_RSA_SINGLE_EXT}, + {VALID_RSA_MULTI_EXT}, + {VALID_DSA_SINGLE_EXT}, + {VALID_DSA_MULTI_EXT}, + {VALID_ECDSA_SINGLE_EXT}, + {VALID_ECDSA_MULTI_EXT}, + {VALID_ED25519_SINGLE_EXT}, + {VALID_ED25519_MULTI_EXT}, + { NULL }, + }; + + for (iter = tests; iter->key != NULL; iter++) { + char *fingerprint = NULL; + size_t len = FingerPrintFromBlob(iter->key, &fingerprint); + ASSERT_GT(len, 0); + ASSERT_STREQ(fingerprint, "b86db4ca-09fd-429e-b121-a12799614032"); + free(fingerprint); + } +} + +TEST(SSHCATests, TestInvalidNoFpCert) { + struct { + const char *key; + } *iter, tests[] = { + {INVALID_DSA_NO_FP}, + {INVALID_DSA_NON_CERT}, + {INVALID_ED25519_NO_FP}, + {INVALID_ED25519_NON_CERT}, + {INVALID_RSA_NO_FP}, + {INVALID_RSA_NON_CERT}, + {INVALID_ECDSA_NO_FP}, + {INVALID_ECDSA_NON_CERT}, + { NULL }, + }; + + for (iter = tests; iter->key != NULL; iter++) { + char *fingerprint = NULL; + size_t len = FingerPrintFromBlob(iter->key, &fingerprint); + ASSERT_EQ(len, 0); + ASSERT_STREQ(fingerprint, NULL); + free(fingerprint); + } +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff -Nru google-compute-engine-oslogin-20220714.00/third_party/include/openbsd.h google-compute-engine-oslogin-20231004.00/third_party/include/openbsd.h --- google-compute-engine-oslogin-20220714.00/third_party/include/openbsd.h 1970-01-01 00:00:00.000000000 +0000 +++ google-compute-engine-oslogin-20231004.00/third_party/include/openbsd.h 2023-10-04 22:45:15.000000000 +0000 @@ -0,0 +1,242 @@ +/* $OpenBSD: base64.c,v 1.5 2006/10/21 09:55:03 otto Exp $ */ + +/* + * Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* OPENBSD ORIGINAL: lib/libc/net/base64.c */ + +#ifndef _OPENBSD_H_ +#define _OPENBSD_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +static char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +int +b64_pton(char const *src, u_char *target, size_t targsize) +{ + u_int tarindex, state; + int ch; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (isspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if (tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = ((pos - Base64) & 0x0f) + << 4 ; + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if (tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = ((pos - Base64) & 0x03) + << 6; + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for (; ch != '\0'; ch = *src++) + if (!isspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for (; ch != '\0'; ch = *src++) + if (!isspace(ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} + +#ifdef __cplusplus +} +#endif +#endif