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

Proposed by Chloé Smith
Status: Merged
Merged at revision: e2cd9a8af8b62b90e6f381fe1eca62d7e80ec03b
Proposed branch: ~kajiya/+git/google-compute-engine-oslogin:update-master-to-20240701.00
Merge into: ~ubuntu-core-dev/+git/google-compute-engine-oslogin:ubuntu/master
Diff against target: 698 lines (+251/-89)
9 files modified
OWNERS (+2/-2)
debian/changelog (+6/-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+469490@code.launchpad.net

Commit message

New upstream version 20240701.00

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

Hey,

Thank you for the MP, it looks good but it doesn't do the right .so replacement.

I am wondering how'd it work for you? Do any of the tests test the .so?

review: Needs Fixing
Revision history for this message
Utkarsh Gupta (utkarsh) wrote :

Ah, I debugged and that's what you need:

commit 421ec6034aebb2f0346d973e4e33a95259583c54 (HEAD -> ubuntu/master, tag: ubuntu/20240701.00-0ubuntu1)
Author: Utkarsh Gupta <email address hidden>
Date: Wed Jul 24 17:19:21 2024 +0900

    d/links: Update links w/ respective .so shipped

diff --git a/debian/changelog b/debian/changelog
index 82fd2b8..f16cf4f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,5 +1,9 @@
 google-compute-engine-oslogin (20240701.00-0ubuntu1) oracular; urgency=medium

+ [ Utkarsh Gupta ]
+ * d/links: Update links w/ respective .so shipped
+
+ [ Chloé 'kajiya' Smith ]
   * New upstream version for upstream tag 20240320.00. (LP: #2073166)

  -- Chloé 'kajiya' Smith <email address hidden> Mon, 15 Jul 2024 17:56:01 +0100
diff --git a/debian/links b/debian/links
index 12e449f..f5a4762 100644
--- a/debian/links
+++ b/debian/links
@@ -1,2 +1,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
+lib/libnss_cache_oslogin-20240701.00.so lib/libnss_cache_oslogin.so.2
+lib/libnss_oslogin-20240701.00.so lib/libnss_oslogin.so.2

Sponsoring your upload with this now.

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

Subscribers

People subscribed via source and target branches