Skip to content

Commit

Permalink
Support additional GECOS field username parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt Schwager committed Jun 19, 2017
1 parent 355f2d9 commit 5f5d0ee
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
/duo_unix-*.tar.*
/duo_unix.spec
/lib/testduo
/lib/testutil_duo_split_at
/libtool
/login_duo/login_duo
/stamp-h1
Expand Down
4 changes: 3 additions & 1 deletion lib/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ notrans_dist_man3_MANS = duo.3
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libduo.pc

noinst_PROGRAMS = testduo
noinst_PROGRAMS = testduo testutil_duo_split_at

testduo_LDADD = libduo.la $(top_builddir)/compat/libcompat.la

testutil_duo_split_at_LDADD = libduo.la $(top_builddir)/compat/libcompat.la

install-data-local:
$(MKDIR_P) $(DESTDIR)$(sysconfdir)
-@if [ ! -f $(DESTDIR)$(sysconfdir)/duo.crt ]; then \
Expand Down
43 changes: 43 additions & 0 deletions lib/testutil_duo_split_at.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "util.h"

int success()
{
printf("OK\n");
return EXIT_SUCCESS;
}

int failure()
{
printf("FAIL\n");
return EXIT_FAILURE;
}

int main (int argc, char *argv[])
{
if (argc != 5) {
printf("Format: %s <string|NULL> <delimiter> <position> <expected|NULL>\n", argv[0]);
return EXIT_FAILURE;
}

char *s = argv[1];
char *delimiter = argv[2];
int position = atoi(argv[3]);
char *expected = argv[4];

if (strcmp(s, "NULL") == 0) {
s = NULL;
}

char *result = duo_split_at(s, *delimiter, position);

if ((result == NULL && strcmp(expected, "NULL") == 0) ||
(result != NULL && strcmp(result, expected) == 0)) {
return success();
}

return failure();
}
27 changes: 26 additions & 1 deletion lib/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@ duo_common_ini_handler(struct duo_config *cfg, const char *section,
cfg->https_timeout *= 1000;
}
} else if (strcmp(name, "send_gecos") == 0) {
cfg->send_gecos = duo_set_boolean_option(val);
cfg->send_gecos = duo_set_boolean_option(val);
} else if (strcmp(name, "gecos_parsed") == 0) {
cfg->gecos_parsed = duo_set_boolean_option(val);
} else {
/* Couldn't handle the option, maybe it's target specific? */
return (0);
Expand Down Expand Up @@ -211,3 +213,26 @@ duo_local_ip()
return (ip);
}

char *
duo_split_at(char *s, char delimiter, unsigned int position)
{
unsigned int count = 0;
char *iter = NULL;
char *result = s;

for (iter = s; *iter; iter++) {
if (*iter == delimiter) {
if (count < position) {
result = iter + 1;
count++;
}
*iter = '\0';
}
}

if (count < position) {
return NULL;
}

return result;
}
3 changes: 3 additions & 0 deletions lib/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ struct duo_config {
int local_ip_fallback;
int https_timeout;
int send_gecos;
int gecos_parsed;
};

void duo_config_default(struct duo_config *cfg);
Expand Down Expand Up @@ -70,4 +71,6 @@ const char *duo_resolve_name(const char *hostname);

const char *duo_local_ip();

char *duo_split_at(char *s, char delimiter, unsigned int position);

#endif
26 changes: 26 additions & 0 deletions login_duo/login_duo.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ do_auth(struct login_ctx *ctx, const char *cmd)
int i, flags, ret, prompts, matched;
int headless = 0;

/*
* Handle a delimited GECOS field. E.g.
*
* username:x:0:0:code1/code2/code3//textField/usergecosparsed:/username:/bin/bash
*
* Parse the username from the appropriate position in the GECOS field.
*/
const char delimiter = '/';
const unsigned int delimited_position = 5;

if ((pw = getpwuid(ctx->uid)) == NULL) {
die("Who are you?");
}
Expand Down Expand Up @@ -174,6 +184,22 @@ do_auth(struct login_ctx *ctx, const char *cmd)
return (EXIT_SUCCESS);
}

/* Use GECOS field if called for */
if (cfg.send_gecos || cfg.gecos_parsed && !ctx->duouser) {
if (strlen(pw->pw_gecos) > 0) {
if (cfg.gecos_parsed) {
duouser = duo_split_at(pw->pw_gecos, delimiter, delimited_position);
if (duouser == NULL || (strcmp(duouser, "") == 0)) {
duo_log(LOG_DEBUG, "Could not parse GECOS field", pw->pw_name, NULL, NULL);
duouser = pw->pw_name;
}
} else {
duouser = pw->pw_gecos;
}
} else {
duo_log(LOG_WARNING, "Empty GECOS field", pw->pw_name, NULL, NULL);
}
}

/* Check for remote login host */
if ((host = ip = getenv("SSH_CONNECTION")) != NULL ||
Expand Down
22 changes: 20 additions & 2 deletions pam_duo/pam_duo.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,16 @@ pam_sm_authenticate(pam_handle_t *pamh, int pam_flags,

int i, flags, pam_err, matched;

/*
* Handle a delimited GECOS field. E.g.
*
* username:x:0:0:code1/code2/code3//textField/usergecosparsed:/username:/bin/bash
*
* Parse the username from the appropriate position in the GECOS field.
*/
const char delimiter = '/';
const unsigned int delimited_position = 5;

duo_config_default(&cfg);

/* Parse configuration */
Expand Down Expand Up @@ -193,9 +203,17 @@ pam_sm_authenticate(pam_handle_t *pamh, int pam_flags,
}

/* Use GECOS field if called for */
if (cfg.send_gecos) {
if (cfg.send_gecos || cfg.gecos_parsed) {
if (strlen(pw->pw_gecos) > 0) {
user = pw->pw_gecos;
if (cfg.gecos_parsed) {
user = duo_split_at(pw->pw_gecos, delimiter, delimited_position);
if (user == NULL || (strcmp(user, "") == 0)) {
duo_log(LOG_DEBUG, "Could not parse GECOS field", pw->pw_name, NULL, NULL);
user = pw->pw_name;
}
} else {
user = pw->pw_gecos;
}
} else {
duo_log(LOG_WARNING, "Empty GECOS field", pw->pw_name, NULL, NULL);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ TESTS_ENVIRONMENT = env BUILDDIR=$(abs_top_builddir) $(PYTHON) $(top_srcdir)/tes

# Preserve ordering; login_duo-0.t does some setup
TESTS = login_duo-0.t login_duo-1.t login_duo-2.t login_duo-3.t login_duo-4.t login_duo-5.t
TESTS += groups-0.t mocklogin_duo-0.t
TESTS += groups-0.t mocklogin_duo-0.t util-0.t
PAM_TESTS = pam_duo-0.t pam_duo-1.t pam_duo-2.t pam_duo-3.t pam_duo-4.t

check_LTLIBRARIES = libgroups_preload.la
Expand Down
35 changes: 35 additions & 0 deletions tests/util-0.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
util.{c,h} unit tests

$ cd ${BUILDDIR}/lib

duo_split_at: basic
$ ./testutil_duo_split_at foo/bar/baz / 1 bar
OK

duo_split_at: first
$ ./testutil_duo_split_at foo/bar/baz / 0 foo
OK

duo_split_at: last
$ ./testutil_duo_split_at foo/bar/baz / 2 baz
OK

duo_split_at: too many
$ ./testutil_duo_split_at foo/bar/baz / 100 NULL
OK

duo_split_at: no delimiter
$ ./testutil_duo_split_at foo / 1 NULL
OK

duo_split_at: starts with delimiter
$ ./testutil_duo_split_at /foo/bar/baz / 0 ""
OK

duo_split_at: ends with delimiter
$ ./testutil_duo_split_at foo/bar/baz/ / 3 ""
OK

duo_split_at: empty
$ ./testutil_duo_split_at "" / 0 ""
OK

0 comments on commit 5f5d0ee

Please sign in to comment.