Merge ~kajiya/+git/google-compute-engine-oslogin:update-focal-to-20240701.00 into ~ubuntu-core-dev/+git/google-compute-engine-oslogin:ubuntu/focal

Proposed by Chloé Smith
Status: Merged
Merged at revision: 40989e89fcedf177ac4af10b9a558e7a2281f81a
Proposed branch: ~kajiya/+git/google-compute-engine-oslogin:update-focal-to-20240701.00
Merge into: ~ubuntu-core-dev/+git/google-compute-engine-oslogin:ubuntu/focal
Diff against target: 779 lines (+313/-91)
12 files modified
OWNERS (+2/-2)
debian/changelog (+42/-0)
debian/links (+2/-2)
debian/patches/0002-inherit-cflags.patch (+23/-0)
debian/patches/series (+1/-0)
packaging/google-compute-engine-oslogin.spec (+1/-0)
src/Makefile (+49/-25)
src/include/oslogin_utils.h (+8/-0)
src/oslogin_utils.cc (+98/-62)
src/pam/pam_oslogin_admin.cc (+53/-0)
src/pam/pam_oslogin_login.cc (+27/-0)
test/oslogin_utils_test.cc (+7/-0)
Reviewer Review Type Date Requested Status
Utkarsh Gupta Approve
Review via email: mp+470197@code.launchpad.net

Commit message

* d/ch entry for focal (LP: #2073166)
* Merge branch 'ubuntu-core-dev/ubuntu/master' into 'ubuntu-core-dev/ubuntu/focal'

To post a comment you must log in.
Revision history for this message
Utkarsh Gupta (utkarsh) wrote :

Thank you, this looks good.

Sponsored:

$ dput ubuntu ../google-compute-engine-oslogin_20240701.00-0ubuntu1\~20.04.0_source.changes
Uploading google-compute-engine-oslogin using ftp to ubuntu (host: upload.ubuntu.com; directory: /ubuntu)
running supported-distribution: check whether the target distribution is currently supported (using distro-info)
{'allowed': ['release', 'proposed', 'backports', 'security'], 'known': ['release', 'proposed', 'updates', 'backports', 'security']}
running required-fields: check whether a field is present and non-empty in the changes file
running checksum: verify checksums before uploading
running suite-mismatch: check the target distribution for common errors
running check-debs: makes sure the upload contains a binary package
running gpg: check GnuPG signatures before the upload
Uploading google-compute-engine-oslogin_20240701.00-0ubuntu1~20.04.0.dsc
Uploading google-compute-engine-oslogin_20240701.00.orig.tar.gz
Uploading google-compute-engine-oslogin_20240701.00-0ubuntu1~20.04.0.debian.tar.xz
Uploading google-compute-engine-oslogin_20240701.00-0ubuntu1~20.04.0_source.buildinfo
Uploading google-compute-engine-oslogin_20240701.00-0ubuntu1~20.04.0_source.changes

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/OWNERS b/OWNERS
2index edde10e..59fca61 100644
3--- a/OWNERS
4+++ b/OWNERS
5@@ -3,14 +3,14 @@
6
7 approvers:
8 - a-crate
9+ - ajorg
10 - bkatyl
11 - chaitanyakulkarni28
12 - dorileo
13 - drewhli
14 - elicriffield
15+ - gaughen
16 - jjerger
17 - karnvadaliya
18 - koln67
19- - quintonamore
20- - vorakl
21 - zmarano
22diff --git a/debian/changelog b/debian/changelog
23index c0fc5a6..76fa049 100644
24--- a/debian/changelog
25+++ b/debian/changelog
26@@ -1,3 +1,45 @@
27+google-compute-engine-oslogin (20240701.00-0ubuntu1~20.04.0) focal; urgency=medium
28+
29+ * No-change rebuild for Focal Fossa (LP: #2073166)
30+
31+ -- Chloé 'kajiya' Smith <chloe.smith@canonical.com> Fri, 26 Jul 2024 15:17:37 +0100
32+
33+google-compute-engine-oslogin (20240701.00-0ubuntu1) oracular; urgency=medium
34+
35+ [ Utkarsh Gupta ]
36+ * d/links: Update links w/ respective .so shipped
37+
38+ [ Chloé 'kajiya' Smith ]
39+ * New upstream version for upstream tag 20240320.00. (LP: #2073166)
40+
41+ -- Chloé 'kajiya' Smith <chloe.smith@canonical.com> Mon, 15 Jul 2024 17:56:01 +0100
42+
43+google-compute-engine-oslogin (20231004.00-0ubuntu5) oracular; urgency=medium
44+
45+ * d/p/0002-inherit-cflags.patch: inherit environment's build flags
46+ (LP: #2071665).
47+
48+ -- Vladimir Petko <vladimir.petko@canonical.com> Tue, 02 Jul 2024 10:15:17 +1200
49+
50+google-compute-engine-oslogin (20231004.00-0ubuntu4) noble; urgency=medium
51+
52+ * No-change rebuild for CVE-2024-3094
53+
54+ -- William Grant <wgrant@ubuntu.com> Mon, 01 Apr 2024 16:49:52 +1100
55+
56+google-compute-engine-oslogin (20231004.00-0ubuntu3) noble; urgency=medium
57+
58+ * No-change rebuild against libcurl4t64
59+
60+ -- Steve Langasek <steve.langasek@ubuntu.com> Sat, 16 Mar 2024 06:24:53 +0000
61+
62+google-compute-engine-oslogin (20231004.00-0ubuntu2) noble; urgency=medium
63+
64+ * d/control: There must be a dependency on `google-guest-agent`
65+ to match the (upstream) Google managed d/control file (LP: #2052438)
66+
67+ -- Chloé 'kajiya' Smith <chloe.smith@canonical.com> Thu, 01 Feb 2024 00:06:38 +0000
68+
69 google-compute-engine-oslogin (20231004.00-0ubuntu1~20.04.3) focal; urgency=medium
70
71 * d/control: There must be a dependency on `google-guest-agent`
72diff --git a/debian/links b/debian/links
73index 12e449f..f5a4762 100644
74--- a/debian/links
75+++ b/debian/links
76@@ -1,2 +1,2 @@
77-lib/libnss_cache_oslogin-20231004.00.so lib/libnss_cache_oslogin.so.2
78-lib/libnss_oslogin-20231004.00.so lib/libnss_oslogin.so.2
79+lib/libnss_cache_oslogin-20240701.00.so lib/libnss_cache_oslogin.so.2
80+lib/libnss_oslogin-20240701.00.so lib/libnss_oslogin.so.2
81diff --git a/debian/patches/0002-inherit-cflags.patch b/debian/patches/0002-inherit-cflags.patch
82new file mode 100644
83index 0000000..900f4e1
84--- /dev/null
85+++ b/debian/patches/0002-inherit-cflags.patch
86@@ -0,0 +1,23 @@
87+Description: use environment build flags.
88+ The upstream makefile does not apply environment makefile flags.
89+ This patch inherits the environment so that packaging-specific
90+ build flags are applied.
91+Author: Vladimir Petko <vladimir.petko@canonical.com>
92+Bug: https://github.com/GoogleCloudPlatform/guest-oslogin/issues/139
93+Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/google-compute-engine-oslogin/+bug/2071665
94+Forwarded: no
95+Last-Update: 2024-07-02
96+
97+--- a/src/Makefile
98++++ b/src/Makefile
99+@@ -3,8 +3,8 @@
100+
101+ CPPFLAGS = -Iinclude -I/usr/include/json-c -I$(TOPDIR)/third_party/include
102+ FLAGS = -fPIC -Wall -g
103+-CFLAGS = $(FLAGS) -Wstrict-prototypes
104+-CXXFLAGS = $(FLAGS)
105++CFLAGS := $(CFLAGS) $(FLAGS) -Wstrict-prototypes
106++CXXFLAGS := $(CXXFLAGS) $(FLAGS)
107+
108+ LDFLAGS = -shared -Wl,-z,defs -Wl,-soname,$(SONAME)
109+ LDLIBS = -lcurl -ljson-c
110diff --git a/debian/patches/series b/debian/patches/series
111index 691fd0b..e023474 100644
112--- a/debian/patches/series
113+++ b/debian/patches/series
114@@ -1 +1,2 @@
115 0001-set-LDFLAGS-to-prevent-undefs.patch
116+0002-inherit-cflags.patch
117diff --git a/packaging/google-compute-engine-oslogin.spec b/packaging/google-compute-engine-oslogin.spec
118index 8f5eac2..5cf6bc6 100644
119--- a/packaging/google-compute-engine-oslogin.spec
120+++ b/packaging/google-compute-engine-oslogin.spec
121@@ -67,6 +67,7 @@ make install DESTDIR=%{buildroot} LIBDIR=/%{_lib} VERSION=%{version} INSTALL_SEL
122 /%{_lib}/libnss_cache_oslogin-%{version}.so
123 /%{_lib}/libnss_oslogin.so.2
124 /%{_lib}/libnss_cache_oslogin.so.2
125+/%{_lib}/security/pam_oslogin_admin.so
126 /%{_lib}/security/pam_oslogin_login.so
127 /usr/bin/google_authorized_keys
128 /usr/bin/google_authorized_keys_sk
129diff --git a/src/Makefile b/src/Makefile
130index a633c7c..4f21719 100644
131--- a/src/Makefile
132+++ b/src/Makefile
133@@ -21,12 +21,33 @@ CRONDIR = /etc/cron.d
134 SYSTEMDDIR = /lib/systemd/system
135 PRESETDIR = /lib/systemd/system-preset
136
137+DEST_LIBDIR = $(LIBDIR)
138+DEST_BINDIR = $(BINDIR)
139+DEST_PAMDIR = $(PAMDIR)
140+DEST_MANDIR = $(MANDIR)
141+DEST_SELINUX = /usr/share/selinux/packages
142+DEST_CRONDIR = $(CRONDIR)
143+DEST_SYSTEMDDIR = $(SYSTEMDDIR)
144+DEST_PRESETDIR = $(PRESETDIR)
145+
146+ifneq ($(DESTDIR),)
147+DEST_LIBDIR = $(DESTDIR)/$(LIBDIR)
148+DEST_BINDIR = $(DESTDIR)/$(BINDIR)
149+DEST_PAMDIR = $(DESTDIR)/$(PAMDIR)
150+DEST_MANDIR = $(DESTDIR)/$(MANDIR)
151+DEST_SELINUX = $(DESTDIR)/usr/share/selinux/packages
152+DEST_CRONDIR = $(DESTDIR)/$(CRONDIR)
153+DEST_SYSTEMDDIR = $(DESTDIR)/$(SYSTEMDDIR)
154+DEST_PRESETDIR = $(DESTDIR)/$(PRESETDIR)
155+endif
156+
157 NSS_OSLOGIN_SONAME = libnss_oslogin.so.2
158 NSS_CACHE_OSLOGIN_SONAME = libnss_cache_oslogin.so.2
159
160 NSS_OSLOGIN = libnss_oslogin-$(VERSION).so
161 NSS_CACHE_OSLOGIN = libnss_cache_oslogin-$(VERSION).so
162
163+PAM_ADMIN = pam_oslogin_admin.so
164 PAM_LOGIN = pam_oslogin_login.so
165
166 BINARIES = google_oslogin_nss_cache google_authorized_keys google_authorized_keys_sk google_authorized_principals
167@@ -34,7 +55,7 @@ BINARIES = google_oslogin_nss_cache google_authorized_keys google_authorized_key
168 .PHONY: all clean install
169 .DEFAULT_GOAL := all
170
171-all: $(NSS_OSLOGIN) $(NSS_CACHE_OSLOGIN) $(PAM_LOGIN) $(BINARIES)
172+all: $(NSS_OSLOGIN) $(NSS_CACHE_OSLOGIN) $(PAM_LOGIN) $(PAM_ADMIN) $(BINARIES)
173
174 clean:
175 rm -f $(BINARIES)
176@@ -52,12 +73,15 @@ $(NSS_CACHE_OSLOGIN): nss/nss_cache_oslogin.o nss/compat/getpwent_r.o oslogin_ut
177
178 # PAM modules
179
180-$(PAM_LOGIN): pam/pam_oslogin_login.o oslogin_sshca.o oslogin_utils.o include/oslogin_sshca.h
181+$(PAM_LOGIN): pam/pam_oslogin_login.o oslogin_sshca.o oslogin_utils.o
182+ $(CXX) $(CXXFLAGS) $(CPPFLAGS) -shared $^ -o $@ $(PAMLIBS)
183+
184+$(PAM_ADMIN): pam/pam_oslogin_admin.o oslogin_sshca.o oslogin_utils.o
185 $(CXX) $(CXXFLAGS) $(CPPFLAGS) -shared $^ -o $@ $(PAMLIBS)
186
187 # Utilities.
188
189-google_authorized_principals: authorized_principals/authorized_principals.o oslogin_utils.o oslogin_sshca.o include/oslogin_sshca.h
190+google_authorized_principals: authorized_principals/authorized_principals.o oslogin_utils.o oslogin_sshca.o
191 $(CXX) $(CXXFLAGS) $(CPPFLAGS) $^ -o $@ $(LDLIBS)
192
193 google_authorized_keys: authorized_keys/authorized_keys.o oslogin_utils.o
194@@ -71,37 +95,37 @@ google_oslogin_nss_cache: cache_refresh/cache_refresh.o oslogin_utils.o
195
196 install: all
197 # Make dirs
198- install -d $(DESTDIR)$(LIBDIR)
199- install -d $(DESTDIR)$(PAMDIR)
200- install -d $(DESTDIR)$(BINDIR)
201- install -d $(DESTDIR)$(MANDIR)/man8
202+ install -d $(DEST_LIBDIR)
203+ install -d $(DEST_PAMDIR)
204+ install -d $(DEST_BINDIR)
205+ install -d $(DEST_MANDIR)/man8
206 # NSS modules
207- install -m 0644 -t $(DESTDIR)$(LIBDIR) $(NSS_OSLOGIN) $(NSS_CACHE_OSLOGIN)
208- ln -sf $(NSS_OSLOGIN) $(DESTDIR)$(LIBDIR)/$(NSS_OSLOGIN_SONAME)
209- ln -sf $(NSS_CACHE_OSLOGIN) $(DESTDIR)$(LIBDIR)/$(NSS_CACHE_OSLOGIN_SONAME)
210+ install -m 0644 -t $(DEST_LIBDIR) $(NSS_OSLOGIN) $(NSS_CACHE_OSLOGIN)
211+ ln -sf $(NSS_OSLOGIN) $(DEST_LIBDIR)/$(NSS_OSLOGIN_SONAME)
212+ ln -sf $(NSS_CACHE_OSLOGIN) $(DEST_LIBDIR)/$(NSS_CACHE_OSLOGIN_SONAME)
213 # PAM modules
214- install -m 0644 -t $(DESTDIR)$(PAMDIR) $(PAM_LOGIN)
215+ install -m 0644 -t $(DEST_PAMDIR) $(PAM_LOGIN) $(PAM_ADMIN)
216 # Binaries
217- install -m 0755 -t $(DESTDIR)$(BINDIR) $(BINARIES)
218+ install -m 0755 -t $(DEST_BINDIR) $(BINARIES)
219 # Manpages
220- install -m 0644 -t $(DESTDIR)$(MANDIR)/man8 $(TOPDIR)/man/nss-oslogin.8 $(TOPDIR)/man/nss-cache-oslogin.8
221- gzip -9f $(DESTDIR)$(MANDIR)/man8/nss-oslogin.8
222- gzip -9f $(DESTDIR)$(MANDIR)/man8/nss-cache-oslogin.8
223- ln -sf nss-oslogin.8.gz $(DESTDIR)$(MANDIR)/man8/$(NSS_OSLOGIN_SONAME).8.gz
224- ln -sf nss-cache-oslogin.8.gz $(DESTDIR)$(MANDIR)/man8/$(NSS_CACHE_OSLOGIN_SONAME).8.gz
225+ install -m 0644 -t $(DEST_MANDIR)/man8 $(TOPDIR)/man/nss-oslogin.8 $(TOPDIR)/man/nss-cache-oslogin.8
226+ gzip -9f $(DEST_MANDIR)/man8/nss-oslogin.8
227+ gzip -9f $(DEST_MANDIR)/man8/nss-cache-oslogin.8
228+ ln -sf nss-oslogin.8.gz $(DEST_MANDIR)/man8/$(NSS_OSLOGIN_SONAME).8.gz
229+ ln -sf nss-cache-oslogin.8.gz $(DEST_MANDIR)/man8/$(NSS_CACHE_OSLOGIN_SONAME).8.gz
230 ifdef INSTALL_SELINUX
231 # SELinux policy package
232- install -d $(DESTDIR)/usr/share/selinux/packages
233- install -m 0644 -t $(DESTDIR)/usr/share/selinux/packages $(TOPDIR)/selinux/oslogin.pp
234+ install -d $(DEST_SELINUX)
235+ install -m 0644 -t $(DEST_SELINUX) $(TOPDIR)/selinux/oslogin.pp
236 endif
237 ifdef INSTALL_CRON
238 # Cache refresh cron
239- install -d $(DESTDIR)$(CRONDIR)
240- install -m 0644 $(TOPDIR)/cron.d $(DESTDIR)$(CRONDIR)/google-compute-engine-oslogin
241+ install -d $(DEST_CRONDIR)
242+ install -m 0644 $(TOPDIR)/cron.d $(DEST_CRONDIR)/google-compute-engine-oslogin
243 else
244 # Cache refresh systemd timer
245- install -d $(DESTDIR)$(SYSTEMDDIR)
246- install -m 0644 -t $(DESTDIR)$(SYSTEMDDIR) $(TOPDIR)/google-oslogin-cache.timer $(TOPDIR)/google-oslogin-cache.service
247- install -d $(DESTDIR)$(PRESETDIR)
248- install -m 0644 -t $(DESTDIR)$(PRESETDIR) $(TOPDIR)/90-google-compute-engine-oslogin.preset
249+ install -d $(DEST_SYSTEMDDIR)
250+ install -m 0644 -t $(DEST_SYSTEMDDIR) $(TOPDIR)/google-oslogin-cache.timer $(TOPDIR)/google-oslogin-cache.service
251+ install -d $(DEST_PRESETDIR)
252+ install -m 0644 -t $(DEST_PRESETDIR) $(TOPDIR)/90-google-compute-engine-oslogin.preset
253 endif
254diff --git a/src/include/oslogin_utils.h b/src/include/oslogin_utils.h
255index 368790e..e7c41fe 100644
256--- a/src/include/oslogin_utils.h
257+++ b/src/include/oslogin_utils.h
258@@ -186,6 +186,9 @@ size_t OnCurlWrite(void* buf, size_t size, size_t nmemb, void* userp);
259 bool HttpGet(const string& url, string* response, long* http_code);
260 bool HttpPost(const string& url, const string& data, string* response,
261 long* http_code);
262+// Based on known MDS status codes returns whether the HTTP request
263+// should be retried or not.
264+bool ShouldRetry(long http_code);
265
266 // Returns whether user_name is a valid OsLogin user name.
267 bool ValidateUserName(const string& user_name);
268@@ -294,6 +297,11 @@ extern void SysLogErr(const char *fmt, ...);
269
270 // AuthoOptions wraps authorization options.
271 struct AuthOptions {
272+ // admin_policy_required determines if a user is only authorized if admin
273+ // policy is available for such a user. i.e. AuthorizeUser() should return
274+ // false if adminLogin is not available.
275+ bool admin_policy_required;
276+
277 // security_key determines if the MDS "/users?..." should use
278 // the view=securityKey parameter.
279 bool security_key;
280diff --git a/src/oslogin_utils.cc b/src/oslogin_utils.cc
281index f8bbf9c..aad560f 100644
282--- a/src/oslogin_utils.cc
283+++ b/src/oslogin_utils.cc
284@@ -12,7 +12,7 @@
285 // See the License for the specific language governing permissions and
286 // limitations under the License.
287
288-// Requires libcurl4-openssl-dev libjson0 and libjson0-dev
289+// Requires libcurl4-openssl-dev, libjson-c5, and libjson-c-dev
290 #include <curl/curl.h>
291 #include <errno.h>
292 #include <grp.h>
293@@ -48,7 +48,10 @@
294 using std::string;
295
296 // Maximum number of retries for HTTP requests.
297-const int kMaxRetries = 1;
298+const int kMaxRetries = 3;
299+
300+// Backoff duration 1 sec between retries.
301+const int kBackoffDuration = 1;
302
303 // Regex for validating user names.
304 static const char kUserNameRegex[] = "^[a-zA-Z0-9._][a-zA-Z0-9._-]{0,31}$";
305@@ -189,11 +192,29 @@ bool NssCache::GetNextGroup(BufferManager* buf, struct group* result, int* errno
306 return ParseJsonToGroup(cached_passwd, result, buf, errnop);
307 }
308
309+// ParseJsonRoot is declared early here, away from the other parsing functions
310+// found later (in the "JSON Parsing" section), so LoadJsonUsersToCache can
311+// take advantage of the improved error handling ParseJsonRoot offers.
312+json_object* ParseJsonRoot(const string& json) {
313+ json_object* root = NULL;
314+ struct json_tokener* tok = json_tokener_new();
315+
316+ root = json_tokener_parse_ex(tok, json.c_str(), -1);
317+ if (root == NULL) {
318+ enum json_tokener_error jerr = json_tokener_get_error(tok);
319+ string error_message = json_tokener_error_desc(jerr);
320+ SysLogErr("Failed to parse root JSON element: \"%s\", from input \"%s\"",
321+ error_message, json);
322+ }
323+
324+ json_tokener_free(tok);
325+ return root;
326+}
327+
328 bool NssCache::LoadJsonUsersToCache(string response) {
329 Reset();
330
331- json_object* root = NULL;
332- root = json_tokener_parse(response.c_str());
333+ json_object* root = ParseJsonRoot(response);
334 if (root == NULL) {
335 return false;
336 }
337@@ -392,6 +413,22 @@ size_t OnCurlWrite(void* buf, size_t size, size_t nmemb, void* userp) {
338 return 0;
339 }
340
341+bool ShouldRetry(long http_code) {
342+ if (http_code == 200) {
343+ // Request returned successfully, no need to retry.
344+ return false;
345+ }
346+ if (http_code == 404) {
347+ // Metadata key does not exist, no point of retrying.
348+ return false;
349+ }
350+ if (http_code == 400) {
351+ // Request parameters are bad, no point of retrying.
352+ return false;
353+ }
354+ return true;
355+}
356+
357 bool HttpDo(const string& url, const string& data, string* response, long* http_code) {
358 if (response == NULL || http_code == NULL) {
359 return false;
360@@ -410,6 +447,10 @@ bool HttpDo(const string& url, const string& data, string* response, long* http_
361 return false;
362 }
363 do {
364+ // Apply backoff strategy before retrying.
365+ if (retry_count > 0) {
366+ sleep(kBackoffDuration);
367+ }
368 response_stream.str("");
369 response_stream.clear();
370 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
371@@ -428,7 +469,7 @@ bool HttpDo(const string& url, const string& data, string* response, long* http_
372 return false;
373 }
374 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, http_code);
375- } while (retry_count++ < kMaxRetries && *http_code == 500);
376+ } while (retry_count++ < kMaxRetries && ShouldRetry(*http_code));
377 curl_slist_free_all(header_list);
378 }
379 *response = response_stream.str();
380@@ -506,14 +547,12 @@ bool ValidatePasswd(struct passwd* result, BufferManager* buf, int* errnop) {
381 // ----------------- JSON Parsing -----------------
382
383 bool ParseJsonToUsers(const string& json, std::vector<string>* result) {
384- json_object* root = NULL;
385- root = json_tokener_parse(json.c_str());
386- if (root == NULL) {
387- return false;
388- }
389-
390 bool ret = false;
391
392+ json_object* root = ParseJsonRoot(json);
393+ if (root == NULL) {
394+ return ret;
395+ }
396 json_object* users = NULL;
397 if (!json_object_object_get_ex(root, "usernames", &users)) {
398 ret = true; // means no users, not invalid.
399@@ -535,19 +574,22 @@ cleanup:
400 }
401
402 bool ParseJsonToGroups(const string& json, std::vector<Group>* result) {
403- json_object* root = NULL;
404- root = json_tokener_parse(json.c_str());
405- if (root == NULL) {
406- return false;
407- }
408-
409 bool ret = false;
410
411- json_object* groups = NULL;
412+ json_object* root = ParseJsonRoot(json);
413+ if (root == NULL) {
414+ return ret;
415+ }
416+ json_object* groups;
417+ json_type groupType;
418 if (!json_object_object_get_ex(root, "posixGroups", &groups)) {
419+ SysLogErr("failed to parse POSIX groups from \"%s\"", json);
420 goto cleanup;
421 }
422- if (json_object_get_type(groups) != json_type_array) {
423+ groupType = json_object_get_type(groups);
424+ if (groupType != json_type_array) {
425+ SysLogErr("parsed unexpected type for field \"posixGroups\"; "
426+ "want a list, got %s", groupType);
427 goto cleanup;
428 }
429 for (int idx = 0; idx < (int)json_object_array_length(groups); idx++) {
430@@ -555,11 +597,12 @@ bool ParseJsonToGroups(const string& json, std::vector<Group>* result) {
431
432 json_object* gid;
433 if (!json_object_object_get_ex(group, "gid", &gid)) {
434+ SysLogErr("failed to parse gid from group %s", json_object_get_string(group));
435 goto cleanup;
436 }
437-
438 json_object* name;
439 if (!json_object_object_get_ex(group, "name", &name)) {
440+ SysLogErr("failed to parse name from group %s", json_object_get_string(group));
441 goto cleanup;
442 }
443
444@@ -589,22 +632,19 @@ cleanup:
445
446 bool ParseJsonToGroup(const string& json, struct group* result, BufferManager*
447 buf, int* errnop) {
448+ bool ret = false;
449 *errnop = EINVAL;
450 int gr_gid = 65535;
451
452- json_object* group = NULL;
453- group = json_tokener_parse(json.c_str());
454+ json_object* group = ParseJsonRoot(json);
455 if (group == NULL) {
456 return false;
457 }
458
459- bool ret = false;
460-
461 json_object* gid;
462 if (!json_object_object_get_ex(group, "gid", &gid)) {
463 goto cleanup;
464 }
465-
466 json_object* name;
467 if (!json_object_object_get_ex(group, "name", &name)) {
468 goto cleanup;
469@@ -631,16 +671,13 @@ cleanup:
470
471 std::vector<string> ParseJsonToSshKeys(const string& json) {
472 std::vector<string> result;
473- json_object* ssh_public_keys = NULL;
474-
475- json_object* root = NULL;
476- root = json_tokener_parse(json.c_str());
477+ json_object* root = ParseJsonRoot(json);
478 if (root == NULL) {
479 return result;
480 }
481
482 // Locate the sshPublicKeys object.
483- json_object* login_profiles = NULL;
484+ json_object* login_profiles;
485 if (!json_object_object_get_ex(root, "loginProfiles", &login_profiles)) {
486 goto cleanup;
487 }
488@@ -649,6 +686,7 @@ std::vector<string> ParseJsonToSshKeys(const string& json) {
489 }
490 login_profiles = json_object_array_get_idx(login_profiles, 0);
491
492+ json_object* ssh_public_keys;
493 if (!json_object_object_get_ex(login_profiles, "sshPublicKeys", &ssh_public_keys)) {
494 goto cleanup;
495 }
496@@ -701,16 +739,14 @@ cleanup:
497
498 std::vector<string> ParseJsonToSshKeysSk(const string& json) {
499 std::vector<string> result;
500- json_object* security_keys = NULL;
501
502- json_object* root = NULL;
503- root = json_tokener_parse(json.c_str());
504+ json_object* root = ParseJsonRoot(json);
505 if (root == NULL) {
506 return result;
507 }
508
509 // Locate the securityKeys array.
510- json_object* login_profiles = NULL;
511+ json_object* login_profiles;
512 if (!json_object_object_get_ex(root, "loginProfiles", &login_profiles)) {
513 goto cleanup;
514 }
515@@ -720,9 +756,11 @@ std::vector<string> ParseJsonToSshKeysSk(const string& json) {
516
517 login_profiles = json_object_array_get_idx(login_profiles, 0);
518
519+ json_object* security_keys;
520 if (!json_object_object_get_ex(login_profiles, "securityKeys", &security_keys)) {
521 goto cleanup;
522 }
523+
524 if (json_object_get_type(security_keys) != json_type_array) {
525 goto cleanup;
526 }
527@@ -757,19 +795,18 @@ cleanup:
528
529 bool ParseJsonToPasswd(const string& json, struct passwd* result, BufferManager*
530 buf, int* errnop) {
531+ bool ret = false;
532 *errnop = EINVAL;
533 json_object* root = NULL;
534 json_object* origroot = NULL;
535
536- origroot = root = json_tokener_parse(json.c_str());
537+ origroot = root = ParseJsonRoot(json);
538 if (root == NULL) {
539 return false;
540 }
541
542- bool ret = false;
543- json_object* posix_accounts = NULL;
544-
545- json_object* login_profiles = NULL;
546+ json_object* posix_accounts;
547+ json_object* login_profiles;
548 // If this is called from getpwent_r, loginProfiles won't be in the response.
549 if (json_object_object_get_ex(root, "loginProfiles", &login_profiles)) {
550 if (json_object_get_type(login_profiles) != json_type_array) {
551@@ -888,17 +925,16 @@ bool AddUsersToGroup(std::vector<string> users, struct group* result,
552 }
553
554 bool ParseJsonToEmail(const string& json, string* email) {
555- json_object* root = NULL;
556- root = json_tokener_parse(json.c_str());
557+ bool ret = false;
558+
559+ json_object* root = ParseJsonRoot(json);
560 if (root == NULL) {
561- return false;
562+ return ret;
563 }
564
565- bool ret = false;
566- json_object* json_email = NULL;
567-
568 // Locate the email object.
569- json_object* login_profiles = NULL;
570+ json_object* login_profiles;
571+ json_object* json_email;
572 if (!json_object_object_get_ex(root, "loginProfiles", &login_profiles)) {
573 goto cleanup;
574 }
575@@ -918,8 +954,7 @@ cleanup:
576 }
577
578 bool ParseJsonToSuccess(const string& json) {
579- json_object* root = NULL;
580- root = json_tokener_parse(json.c_str());
581+ json_object* root = ParseJsonRoot(json);
582 if (root == NULL) {
583 return false;
584 }
585@@ -934,17 +969,15 @@ bool ParseJsonToSuccess(const string& json) {
586 }
587
588 bool ParseJsonToKey(const string& json, const string& key, string* response) {
589- json_object* root = NULL;
590- root = json_tokener_parse(json.c_str());
591+ bool ret = false;
592+
593+ json_object* root = ParseJsonRoot(json);
594 if (root == NULL) {
595- return false;
596+ return ret;
597 }
598
599- bool ret = false;
600 json_object* json_response = NULL;
601 const char* c_response = NULL;
602-
603-
604 if (!json_object_object_get_ex(root, key.c_str(), &json_response)) {
605 goto cleanup;
606 }
607@@ -962,13 +995,13 @@ cleanup:
608 }
609
610 bool ParseJsonToChallenges(const string& json, std::vector<Challenge>* challenges) {
611- json_object* root = NULL;
612- root = json_tokener_parse(json.c_str());
613+ bool ret = false;
614+
615+ json_object* root = ParseJsonRoot(json);
616 if (root == NULL) {
617- return false;
618+ return ret;
619 }
620
621- bool ret = false;
622 json_object* challengeId = NULL;
623 json_object* challengeType = NULL;
624 json_object* challengeStatus = NULL;
625@@ -1260,18 +1293,18 @@ static bool ApplyPolicy(const char *user_name, string email, const char *policy,
626 long http_code = 0;
627 // Invalid user, just leave from here - the principal will not be allowed/authorized.
628 if (!HttpGet(url.str(), &response, &http_code)) {
629- SysLogErr("Failed to validate organization user %s has login permission.", user_name);
630+ SysLogErr("Failed to validate that OS Login user %s has %s permission.", user_name, policy);
631 return false;
632 }
633
634 if (http_code != 200) {
635- SysLogErr("Failed to validate organization user %s has login permission, "
636- "got HTTP response code: %lu", user_name, http_code);
637+ SysLogErr("Failed to validate that OS Login user %s has %s permission; "
638+ "got HTTP response code: %lu", user_name, policy, http_code);
639 return false;
640 }
641
642 if (!ParseJsonToSuccess(response)) {
643- SysLogErr("Organization user %s does not have login permission.", user_name);
644+ SysLogErr("OS Login user %s does not have %s permission.", user_name, policy);
645 return false;
646 }
647
648@@ -1369,6 +1402,9 @@ bool AuthorizeUser(const char *user_name, struct AuthOptions opts, string *user_
649 }
650 } else {
651 remove(sudoers_filename.c_str());
652+ if (opts.admin_policy_required) {
653+ return false;
654+ }
655 }
656
657 return true;
658diff --git a/src/pam/pam_oslogin_admin.cc b/src/pam/pam_oslogin_admin.cc
659new file mode 100644
660index 0000000..06bb10b
661--- /dev/null
662+++ b/src/pam/pam_oslogin_admin.cc
663@@ -0,0 +1,53 @@
664+// Copyright 2024 Google Inc. All Rights Reserved.
665+//
666+// Licensed under the Apache License, Version 2.0 (the "License");
667+// you may not use this file except in compliance with the License.
668+// You may obtain a copy of the License at
669+//
670+// http://www.apache.org/licenses/LICENSE-2.0
671+//
672+// Unless required by applicable law or agreed to in writing, software
673+// distributed under the License is distributed on an "AS IS" BASIS,
674+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
675+// See the License for the specific language governing permissions and
676+// limitations under the License.
677+
678+#include <security/pam_modules.h>
679+
680+#include <compat.h>
681+#include <oslogin_utils.h>
682+
683+using std::string;
684+
685+using oslogin_utils::AuthOptions;
686+
687+extern "C" {
688+
689+// pm_sm_acct_mgmt is the account management PAM implementation for admin users (or users
690+// with the proper loginAdmin policy). This account management module is intended for custom
691+// configuration handling only, where users need a way to in their stack configurations to
692+// differentiate a OS Login user. The Google Guest Agent will not manage the lifecycle of
693+// this module, it will not add this to the stack as part of the standard/default configuration
694+// set.
695+PAM_EXTERN int
696+pam_sm_acct_mgmt(pam_handle_t* pamh, int flags, int argc, const char** argv) {
697+ struct AuthOptions opts;
698+ const char *user_name;
699+ string user_response;
700+
701+ if (pam_get_user(pamh, &user_name, NULL) != PAM_SUCCESS) {
702+ PAM_SYSLOG(pamh, LOG_INFO, "Could not get pam user.");
703+ return PAM_PERM_DENIED;
704+ }
705+
706+ opts = { 0 };
707+ opts.admin_policy_required = true;
708+
709+ if (!AuthorizeUser(user_name, opts, &user_response)) {
710+ return PAM_PERM_DENIED;
711+ }
712+
713+ return PAM_SUCCESS;
714+}
715+
716+}
717diff --git a/src/pam/pam_oslogin_login.cc b/src/pam/pam_oslogin_login.cc
718index ca41ed3..5d863d2 100644
719--- a/src/pam/pam_oslogin_login.cc
720+++ b/src/pam/pam_oslogin_login.cc
721@@ -22,6 +22,7 @@
722 #include <compat.h>
723 #include <oslogin_utils.h>
724
725+using oslogin_utils::AuthOptions;
726 using oslogin_utils::ContinueSession;
727 using oslogin_utils::GetUser;
728 using oslogin_utils::ParseJsonToChallenges;
729@@ -32,6 +33,32 @@ using oslogin_utils::ValidateUserName;
730
731 extern "C" {
732
733+// pm_sm_acct_mgmt is the account management PAM implementation for non-admin users (or users
734+// without the proper loginAdmin policy). This account management module is intended for custom
735+// configuration handling only, where users need a way to in their stack configurations to
736+// differentiate a OS Login user. The Google Guest Agent will not manage the lifecycle of
737+// this module, it will not add this to the stack as part of the standard/default configuration
738+// set.
739+PAM_EXTERN int
740+pam_sm_acct_mgmt(pam_handle_t* pamh, int flags, int argc, const char** argv) {
741+ struct AuthOptions opts;
742+ const char *user_name;
743+ string user_response;
744+
745+ if (pam_get_user(pamh, &user_name, NULL) != PAM_SUCCESS) {
746+ PAM_SYSLOG(pamh, LOG_INFO, "Could not get pam user.");
747+ return PAM_PERM_DENIED;
748+ }
749+
750+ opts = { 0 };
751+
752+ if (!AuthorizeUser(user_name, opts, &user_response)) {
753+ return PAM_PERM_DENIED;
754+ }
755+
756+ return PAM_SUCCESS;
757+}
758+
759 PAM_EXTERN int
760 pam_sm_setcred(pam_handle_t* pamh, int flags, int argc, const char** argv) {
761 return PAM_SUCCESS;
762diff --git a/test/oslogin_utils_test.cc b/test/oslogin_utils_test.cc
763index 3451f61..0646a34 100644
764--- a/test/oslogin_utils_test.cc
765+++ b/test/oslogin_utils_test.cc
766@@ -460,6 +460,13 @@ TEST(GetGroupByTest, GetGroupByGIDSucceeds) {
767 ASSERT_EQ(errnop, 0);
768 }
769
770+TEST(CurlClient, RetryLogic) {
771+ ASSERT_FALSE(ShouldRetry(200));
772+ ASSERT_FALSE(ShouldRetry(404));
773+ ASSERT_FALSE(ShouldRetry(400));
774+ ASSERT_TRUE(ShouldRetry(429));
775+}
776+
777 TEST(ParseJsonEmailTest, SuccessfullyParsesEmail) {
778 string test_user =
779 "{\"loginProfiles\":[{\"name\":\"foo@example.com\",\"posixAccounts\":["

Subscribers

People subscribed via source and target branches