diff --git a/src/Makefile b/src/Makefile index 3847c36..555d3e1 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,6 +2,7 @@ # Makefile for the NSS and PAM modules used in Local EGA # # Blowfish code from http://www.openwall.com/crypt/ +# JSON parser code from https://github.com/zserge/jsmn # NSS_LD_SONAME=-Wl,-soname,libnss_ega.so.2 @@ -25,6 +26,10 @@ EGA_LIBDIR=/usr/local/lib/ega EGA_BINDIR=/usr/local/bin EGA_PAMDIR=/lib/security +ifdef NSS_CFGFILE +CFLAGS += -DCFGFILE=\"$(NSS_CFGFILE)\" +endif + HEADERS = utils.h config.h cache.h json.h cega.h $(wildcard jsmn/*.h) $(wildcard blowfish/*.h) NSS_SOURCES = nss.c config.c cache.c json.c cega.c $(wildcard jsmn/*.c) @@ -87,7 +92,7 @@ blowfish/x86.o: blowfish/x86.S @echo "Compiling $<" @$(CC) $(CFLAGS) -c -o $@ $< -install-nss: $(NSS_LIBRARY) +install-nss: $(NSS_LIBRARY) | $(EGA_LIBDIR) @echo "Installing $< into $(EGA_LIBDIR)" @install $< $(EGA_LIBDIR) @@ -110,7 +115,7 @@ install-keys: $(KEYS_EXEC) | $(EGA_BINDIR) @install -m 700 $< $(EGA_BINDIR) install: install-nss install-pam install-keys - @echo "Do not forget to run ldconfig and create/configure the file /etc/ega/auth.conf" + @echo "Do not forget to run ldconfig and create/configure the file $(NSS_CFGFILE)" @echo "Look at the auth.conf.sample here, for example" clean: diff --git a/src/cega.c b/src/cega.c index b2be4fc..b7f44cb 100644 --- a/src/cega.c +++ b/src/cega.c @@ -37,8 +37,9 @@ curl_callback (void* contents, size_t size, size_t nmemb, void* userdata) { return realsize; } + int -cega_resolve(const char *endpoint, int (*cb)(struct fega_user *user)) +cega_resolve(const char *endpoint, struct cb_ctx *ctx) { int rc = 1; /* error */ struct curl_res_s* cres = NULL; @@ -116,14 +117,15 @@ cega_resolve(const char *endpoint, int (*cb)(struct fega_user *user)) if( !user.pwdh && !user.pubkeys ) rc++; if( user.uid <= 0 ) rc++; /* if( !user.gecos ) rc++; */ - if( !user.gecos ) user.gecos = strdup("FEGA User"); + if( !user.gecos ) user.gecos = strdup("FEGA User"); // care about ENOMEM ? if(rc) { D1("We found %d errors", rc); goto BAILOUT; } + D1("Shift %d to %d", user.uid, user.uid + options->uid_shift); user.uid += options->uid_shift; /* Callback: What to do with the data */ - rc = cb(&user); + rc = ctx->fn(ctx, &user); BAILOUT: if(cres->body)free(cres->body); diff --git a/src/cega.h b/src/cega.h index 8850b09..6b736f3 100644 --- a/src/cega.h +++ b/src/cega.h @@ -5,6 +5,25 @@ #include "json.h" -int cega_resolve(const char *endpoint, int (*cb)(struct fega_user *)); +#define CEGA_CALLBACK_UID (1 << 0) +#define CEGA_CALLBACK_NAME (1 << 1) +#define CEGA_CALLBACK_PASSWD (1 << 2) +#define CEGA_CALLBACK_SHADOW (1 << 3) +#define CEGA_CALLBACK_KEYS (1 << 4) + +struct cb_ctx { + int flags; + int (*fn)(struct cb_ctx *, struct fega_user *); + uid_t uid; + const char* username; + struct passwd *passwd; + struct spwd *shadow; + char *buffer; + size_t buflen; + int use_cache; +}; + +int cega_resolve(const char *endpoint, struct cb_ctx *); #endif /* !__FEGA_CENTRAL_H_INCLUDED__ */ + diff --git a/src/config.c b/src/config.c index 5d3a94b..6155aac 100644 --- a/src/config.c +++ b/src/config.c @@ -8,7 +8,9 @@ #include "utils.h" #include "config.h" +#ifndef CFGFILE #define CFGFILE "/etc/ega/auth.conf" +#endif #define CACHE_TTL 3600 // 1h in seconds. #define EGA_UID_SHIFT 10000 @@ -129,15 +131,15 @@ readconfig(FILE* fp, char* buffer, size_t buflen) } else val = NULL; /* could not find the '=' sign */ - if(!strcmp(key, "ega_uid_shift" )) { if( !sscanf(val, "%u" , &(options->uid_shift) )) options->uid_shift = -1; } - if(!strcmp(key, "cache_ttl" )) { if( !sscanf(val, "%u" , &(options->cache_ttl) )) options->cache_ttl = -1; } - if(!strcmp(key, "gid" )) { if( !sscanf(val, "%u" , &(options->gid) )) options->gid = -1; } - - if(!strcmp(key, "shadow_min" )) { if( !sscanf(val, "%ld" , &(options->sp_min) )) options->sp_min = 0; } - if(!strcmp(key, "shadow_max" )) { if( !sscanf(val, "%ld" , &(options->sp_max) )) options->sp_max = 0; } - if(!strcmp(key, "shadow_warn" )) { if( !sscanf(val, "%ld" , &(options->sp_warn) )) options->sp_warn = -1l; } - if(!strcmp(key, "shadow_inact" )) { if( !sscanf(val, "%ld" , &(options->sp_inact) )) options->sp_inact = -1l; } - if(!strcmp(key, "shadow_expire" )) { if( !sscanf(val, "%ld" , &(options->sp_expire) )) options->sp_expire = -1l; } + if(!strcmp(key, "uid_shift")) { if( !sscanf(val, "%u" , &(options->uid_shift) )) options->uid_shift = -1; } + if(!strcmp(key, "cache_ttl")) { if( !sscanf(val, "%u" , &(options->cache_ttl) )) options->cache_ttl = -1; } + if(!strcmp(key, "gid" )) { if( !sscanf(val, "%u" , &(options->gid) )) options->gid = -1; } + + if(!strcmp(key, "shadow_min" )) { if( !sscanf(val, "%ld" , &(options->sp_min) )) options->sp_min = 0; } + if(!strcmp(key, "shadow_max" )) { if( !sscanf(val, "%ld" , &(options->sp_max) )) options->sp_max = 0; } + if(!strcmp(key, "shadow_warn" )) { if( !sscanf(val, "%ld" , &(options->sp_warn) )) options->sp_warn = -1l; } + if(!strcmp(key, "shadow_inact" )) { if( !sscanf(val, "%ld" , &(options->sp_inact) )) options->sp_inact = -1l; } + if(!strcmp(key, "shadow_expire")) { if( !sscanf(val, "%ld" , &(options->sp_expire) )) options->sp_expire = -1l; } INJECT_OPTION(key, "db_path" , val, &(options->db_path) ); INJECT_OPTION(key, "homedir_prefix" , val, &(options->homedir_prefix) ); diff --git a/src/keys.c b/src/keys.c index fc43521..c777218 100644 --- a/src/keys.c +++ b/src/keys.c @@ -5,6 +5,34 @@ #include "cache.h" #include "cega.h" +/* Defining the CentralEGA callback */ +int print_pubkey(struct cb_ctx *ctx, struct fega_user *user){ + + + /* assert( (flags & CEGA_CALLBACK_KEYS) */ + /* && */ + /* (flags & CEGA_CALLBACK_NAME) ); */ + + if( strcmp(ctx->username, user->username) ){ + REPORT("Requested username %s not matching username response %s", ctx->username, user->username); + return 1; + } + + if(ctx->use_cache) cache_add_user(user); // ignore result + + if(user->pubkeys){ + struct pbk *current = user->pubkeys; + while( current ){ + printf("%s\n", current->pbk); + current = current->next; + } + } else { + REPORT("No ssh key found for user '%s'", ctx->username); + } + + return 0; +} + int main(int argc, const char **argv) { @@ -18,29 +46,6 @@ main(int argc, const char **argv) bool use_cache = options->use_cache && cache_open(); if(use_cache && cache_print_pubkeys(username)) return rc; - REPORT("Fetching the public keys from CentralEGA"); - - /* Defining the CentralEGA callback */ - int print_pubkey(struct fega_user *user){ - - /* assert same name */ - if( strcmp(username, user->username) ){ - REPORT("Requested username %s not matching username response %s", username, user->username); - return 1; - } - if(user->pubkeys){ - struct pbk *current = user->pubkeys; - while( current ){ - printf("%s\n", current->pbk); - current = current->next; - } - } else { - REPORT("No ssh key found for user '%s'", username); - } - if(use_cache) cache_add_user(user); // ignore result - return 0; - } - char* endpoint = (char*)malloc((options->cega_endpoint_username_len + strlen(username)) * sizeof(char)); if(!endpoint){ D1("Memory allocation error"); return 1; } @@ -49,8 +54,17 @@ main(int argc, const char **argv) free(endpoint); return 2; } + struct cb_ctx ctx = { .flags = CEGA_CALLBACK_KEYS | CEGA_CALLBACK_NAME, + .fn = &print_pubkey, + .uid = -1, + .username = username, + .passwd = NULL, + .shadow = NULL, + .buffer = NULL, + .buflen = 0, + .use_cache = use_cache }; - rc = cega_resolve(endpoint, print_pubkey); + rc = cega_resolve(endpoint, &ctx); free(endpoint); return rc; } diff --git a/src/nss.c b/src/nss.c index d170042..0580d51 100644 --- a/src/nss.c +++ b/src/nss.c @@ -22,6 +22,69 @@ enum nss_status NSS_NAME(setpwent)(int stayopen){ D1("called"); return NSS_STATUS_UNAVAIL; } enum nss_status NSS_NAME(endpwent)(void){ D1("called"); return NSS_STATUS_UNAVAIL; } enum nss_status NSS_NAME(getpwent_r)(struct passwd *result, char *buffer, size_t buflen, int *errnop){ D1("called"); return NSS_STATUS_UNAVAIL; } + + +static int +cega_callback(struct cb_ctx *ctx, struct fega_user *user) +{ + + if(ctx->flags & CEGA_CALLBACK_UID){ + if( user->uid != ctx->uid ){ + REPORT("Requested user id %u not matching user id response %u", ctx->uid, user->uid); + return 1; + } + } + + if(ctx->flags & CEGA_CALLBACK_NAME){ + if( strcmp(user->username, ctx->username) ){ + REPORT("Requested username %s not matching username response %s", ctx->username, user->username); + return 1; + } + } + + /* Add to database. Ignore result. + In case the buffer is too small later, it'll fetch the same data from the cache, next time. */ + if(ctx->use_cache) cache_add_user(user); + + /* Prepare the answer */ + char* homedir = strjoina(options->homedir_prefix, "/", user->username); + D1("User id %u [Username %s] [Homedir %s]", user->uid, user->username, homedir); + + char *buffer = ctx->buffer; + size_t buflen = ctx->buflen; + + if(ctx->flags & CEGA_CALLBACK_PASSWD){ + + // Note: can use ctx->username, without copy, when (flags & CEGA_CALLBACK_NAME) + // but... don't care... + if( copy2buffer(user->username, &(ctx->passwd->pw_name) , &buffer, &buflen) < 0 ) { return -1; } + if( copy2buffer("x", &(ctx->passwd->pw_passwd), &buffer, &buflen) < 0 ){ return -1; } + ctx->passwd->pw_uid = user->uid; + ctx->passwd->pw_gid = options->gid; + if( copy2buffer(homedir, &(ctx->passwd->pw_dir) , &buffer, &buflen) < 0 ) { return -1; } + if( copy2buffer(user->gecos, &(ctx->passwd->pw_gecos) , &buffer, &buflen) < 0 ) { return -1; } + if( copy2buffer(options->shell, &(ctx->passwd->pw_shell), &buffer, &buflen) < 0 ) { return -1; } + return 0; + } + + if((ctx->flags & CEGA_CALLBACK_SHADOW) + && (ctx->flags & CEGA_CALLBACK_NAME)){ + + if( copy2buffer(user->username, &(ctx->shadow->sp_namp) , &buffer, &buflen) < 0 ) { return -1; } + if( copy2buffer(user->pwdh, &(ctx->shadow->sp_pwdp), &buffer, &buflen) < 0 ){ return -1; } + ctx->shadow->sp_lstchg = user->last_changed; + ctx->shadow->sp_min = options->sp_min; + ctx->shadow->sp_max = options->sp_max; + ctx->shadow->sp_warn = options->sp_warn; + ctx->shadow->sp_inact = options->sp_inact; + ctx->shadow->sp_expire = options->sp_expire; + return 0; + } + + REPORT("Invalid callback mode [flags: %d]", ctx->flags); + return 1; +} + enum nss_status NSS_NAME(getpwuid_r)(uid_t uid, struct passwd *result, @@ -49,33 +112,16 @@ NSS_NAME(getpwuid_r)(uid_t uid, struct passwd *result, } D1("Fetching user from CentralEGA"); + struct cb_ctx ctx = { .flags = CEGA_CALLBACK_PASSWD | CEGA_CALLBACK_UID, + .fn = &cega_callback, + .uid = uid, + .username = NULL, + .passwd = result, + .shadow = NULL, + .buffer = buffer, + .buflen = buflen, + .use_cache = use_cache }; - /* Defining the callback */ - int cega_callback(struct fega_user *user){ - - /* assert same name */ - if( user->uid != uid ){ - REPORT("Requested user id %u not matching user id response %u", uid, user->uid); - return 1; - } - - /* Add to database. Ignore result. - In case the buffer is too small later, it'll fetch the same data from the cache, next time. */ - if(use_cache) cache_add_user(user); - - /* Prepare the answer */ - char* homedir = strjoina(options->homedir_prefix, "/", user->username); - D1("User id %u [Username %s] [Homedir %s]", user->uid, user->username, homedir); - if( copy2buffer(user->username, &(result->pw_name) , &buffer, &buflen) < 0 ) { return -1; } - if( copy2buffer("x", &(result->pw_passwd), &buffer, &buflen) < 0 ){ return -1; } - result->pw_uid = user->uid; - result->pw_gid = options->gid; - if( copy2buffer(homedir, &(result->pw_dir) , &buffer, &buflen) < 0 ) { return -1; } - if( copy2buffer(user->gecos, &(result->pw_gecos) , &buffer, &buflen) < 0 ) { return -1; } - if( copy2buffer(options->shell, &(result->pw_shell), &buffer, &buflen) < 0 ) { return -1; } - - return 0; - } char* endpoint = (char*)malloc((options->cega_endpoint_uid_len + 32) * sizeof(char)); /* Laaaaaaaarge enough! */ @@ -85,7 +131,7 @@ NSS_NAME(getpwuid_r)(uid_t uid, struct passwd *result, D1("Error formatting the endpoint"); return NSS_STATUS_NOTFOUND; } - rc = cega_resolve(endpoint, cega_callback); + rc = cega_resolve(endpoint, &ctx); free(endpoint); if( rc == -1 ){ D1("Buffer too small"); *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } if( rc > 0 ) { D1("User id %u not found in CentralEGA", uid); return NSS_STATUS_NOTFOUND; } @@ -93,6 +139,7 @@ NSS_NAME(getpwuid_r)(uid_t uid, struct passwd *result, return NSS_STATUS_SUCCESS; } + /* Find user ny name */ enum nss_status NSS_NAME(getpwnam_r)(const char *username, struct passwd *result, @@ -117,33 +164,15 @@ NSS_NAME(getpwnam_r)(const char *username, struct passwd *result, } D1("Fetching user from CentralEGA"); - - /* Defining the callback */ - int cega_callback(struct fega_user *user){ - - /* assert same name */ - if( strcmp(username, user->username) ){ - REPORT("Requested username %s not matching username response %s", username, user->username); - return 1; - } - - /* Add to database. Ignore result. - In case the buffer is too small later, it'll fetch the same data from the cache, next time. */ - if(use_cache) cache_add_user(user); - - /* Prepare the answer */ - char* homedir = strjoina(options->homedir_prefix, "/", username); - D1("Username %s [Homedir %s]", user->username, homedir); - result->pw_name = (char*)username; /* no need to copy to buffer */ - if( copy2buffer("x", &(result->pw_passwd), &buffer, &buflen) < 0 ){ return -1; } - result->pw_uid = user->uid; - result->pw_gid = options->gid; - if( copy2buffer(homedir, &(result->pw_dir) , &buffer, &buflen) < 0 ) { return -1; } - if( copy2buffer(user->gecos, &(result->pw_gecos) , &buffer, &buflen) < 0 ) { return -1; } - if( copy2buffer(options->shell, &(result->pw_shell), &buffer, &buflen) < 0 ) { return -1; } - - return 0; - } + struct cb_ctx ctx = { .flags = CEGA_CALLBACK_PASSWD | CEGA_CALLBACK_NAME, + .fn = &cega_callback, + .uid = -1, + .username = username, + .passwd = result, + .shadow = NULL, + .buffer = buffer, + .buflen = buflen, + .use_cache = use_cache }; char* endpoint = (char*)malloc((options->cega_endpoint_username_len + strlen(username)) * sizeof(char)); if(!endpoint){ D1("Memory allocation error"); return NSS_STATUS_NOTFOUND; } @@ -152,7 +181,7 @@ NSS_NAME(getpwnam_r)(const char *username, struct passwd *result, D1("Error formatting the endpoint"); return NSS_STATUS_NOTFOUND; } - rc = cega_resolve(endpoint, cega_callback); + rc = cega_resolve(endpoint, &ctx); free(endpoint); if( rc == -1 ){ D1("Buffer too small"); *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } if( rc > 0 ) { D1("User %s not found in CentralEGA", username); return NSS_STATUS_NOTFOUND; } @@ -202,32 +231,16 @@ NSS_NAME(getspnam_r)(const char *username, struct spwd *result, D1("Fetching user from CentralEGA"); + struct cb_ctx ctx = { .flags = CEGA_CALLBACK_SHADOW | CEGA_CALLBACK_NAME, + .fn = &cega_callback, + .uid = -1, + .username = username, + .passwd = NULL, + .shadow = result, + .buffer = buffer, + .buflen = buflen, + .use_cache = use_cache }; - /* Defining the callback */ - int cega_callback(struct fega_user *user){ - - /* assert same name */ - if( strcmp(username, user->username) ){ - REPORT("Requested username %s not matching username response %s", username, user->username); - return 1; - } - - /* Add to database. Ignore result. - In case the buffer is too small later, it'll fetch the same data from the cache, next time. */ - if(use_cache) cache_add_user(user); - - /* Prepare the answer */ - result->sp_namp = (char*)username; /* no need to copy to buffer */ - if( copy2buffer(user->pwdh, &(result->sp_pwdp), &buffer, &buflen) < 0 ){ return -1; } - result->sp_lstchg = user->last_changed; - result->sp_min = options->sp_min; - result->sp_max = options->sp_max; - result->sp_warn = options->sp_warn; - result->sp_inact = options->sp_inact; - result->sp_expire = options->sp_expire; - - return 0; - } char* endpoint = (char*)malloc((options->cega_endpoint_username_len + strlen(username)) * sizeof(char)); if(!endpoint){ D1("Memory allocation error"); return NSS_STATUS_NOTFOUND; } @@ -236,7 +249,7 @@ NSS_NAME(getspnam_r)(const char *username, struct spwd *result, D1("Error formatting the endpoint"); return NSS_STATUS_NOTFOUND; } - rc = cega_resolve(endpoint, cega_callback); + rc = cega_resolve(endpoint, &ctx); free(endpoint); if( rc == -1 ){ D1("Buffer too small"); *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } if( rc > 0 ) { D1("User %s not found in CentralEGA", username); return NSS_STATUS_NOTFOUND; } diff --git a/src/pam_acct.c b/src/pam_acct.c index e1961ba..f808084 100644 --- a/src/pam_acct.c +++ b/src/pam_acct.c @@ -3,7 +3,7 @@ #include #include #include -#include /* PATH_MAX and NGROUPS_MAX*/ +#include #include #include @@ -23,6 +23,7 @@ struct options_s { int flags; mode_t attrs; + gid_t swap_gid; }; PAM_EXTERN int @@ -36,7 +37,8 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char *argv[]) D3("Getting account PAM module options"); /* defaults */ opts.flags = 0; - opts.attrs = 0755; /* rwxr-xr-x */ + opts.attrs = 0750; /* rwxr-x--- */ + opts.swap_gid = -1; /* Step through module arguments */ for (; argc-- > 0; ++argv){ @@ -46,8 +48,25 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char *argv[]) opts.flags |= EGA_OPT_SILENT; } else if (!strcmp(*argv, "bail_on_exists")) { opts.flags |= EGA_BAIL_ON_EXISTS; + } else if (!strcmp(*argv, "force_attrs")) { + opts.flags |= EGA_ENFORCE_ATTRS; } else if (!strncmp(*argv, "attrs=", 6)) { opts.attrs = (mode_t)strtol(*argv+6, NULL, 8); /* octal */ + } else if (!strncmp(*argv, "swap_group=",11)) { + struct group *g = getgrnam(*argv+11); + if(g == NULL){ + D1("unknown supplementary group: %s", *argv+11); + continue; + } + opts.swap_gid = g->gr_gid; + } else if (!strncmp(*argv, "swap_gid=", 9)) { + gid_t gid = (gid_t)strtol(*argv+9, NULL, 10); + struct group *g = getgrgid(gid); + if(g == NULL){ + D1("unknown supplementary group: %s", *argv+9); + //continue; + } //else + opts.swap_gid = gid; } else { D1("unknown option: %s", *argv); /* pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); */ @@ -88,7 +107,7 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char *argv[]) } /* enforce ownership */ - rc = chown(user->pw_dir, user->pw_uid, user->pw_gid); + rc = chown(user->pw_dir, user->pw_uid, (opts.swap_gid > 0) ? opts.swap_gid : user->pw_gid); if(rc){ D2("unable to change ownership to root | rc: %d | %s", rc, strerror(errno)); return PAM_PERM_DENIED; diff --git a/src/pam_session.c b/src/pam_session.c index ff361b7..91eaab1 100644 --- a/src/pam_session.c +++ b/src/pam_session.c @@ -6,7 +6,9 @@ #include #include #include +#include #include /* for umask */ +#include /* PATH_MAX and NGROUPS_MAX*/ #define PAM_SM_SESSION #include @@ -16,9 +18,14 @@ #include "utils.h" +#define EGA_OPT_SILENT (1) +#define EGA_OPT_DEBUG (1 << 1) +#define EGA_OPT_INIT_GROUPS (1 << 2) + struct options_s { int flags; mode_t umask; + gid_t swap_gid; }; /* @@ -29,10 +36,25 @@ void pam_options(struct options_s *opts, int argc, const char **argv) char** args = (char**)argv; /* Step through module arguments */ for (; argc-- > 0; ++args){ - /* if (!strcmp(*args, "silent")) { */ - /* opts->flags |= PAM_SILENT; */ - /* } else */ if (!strncmp(*argv, "umask=", 6)) { + if (!strcmp(*args, "silent")) { + opts->flags |= EGA_OPT_SILENT; + } else if (!strncmp(*argv, "umask=", 6)) { opts->umask = (mode_t)strtol(*argv+6, NULL, 8); /* octal */ + } else if (!strncmp(*argv, "swap_group=",11)) { + struct group *g = getgrnam(*argv+11); + if(g == NULL){ + D1("unknown supplementary group: %s", *argv+11); + continue; + } + opts->swap_gid = g->gr_gid; + } else if (!strncmp(*argv, "swap_gid=", 9)) { + gid_t gid = (gid_t)strtol(*argv+9, NULL, 10); + struct group *g = getgrgid(gid); + if(g == NULL){ + D1("unknown supplementary group: %s", *argv+9); + //continue; + } //else + opts->swap_gid = gid; } else { D1("unknown option: %s", *args); } @@ -52,7 +74,8 @@ pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) /* defaults */ opts.flags = 0; - opts.umask = 0; + opts.umask = umask(0); // old mask + opts.swap_gid = -1; D2("Getting open session PAM module options"); pam_options(&opts, argc, argv); @@ -66,6 +89,17 @@ pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) /* Handling umask */ D1("Setting umask to %o", opts.umask); umask((mode_t)opts.umask); /* ignore old mask */ + + /* Swap for the other group before creating the directory. + Otherwise, CephFS doesn't respect the setgid, since the user won't belong to the fega group. + Using ACLs doesn't work because CephFS will make the mask --- (instead of the group:fega:r-x) + */ + if (opts.swap_gid > 0 && setregid(opts.swap_gid, opts.swap_gid)){ + D1("Error swapping main group for user %s", user->pw_name); + D2("from %d to %d", user->pw_gid, opts.swap_gid); + D1("errno %d: %s", errno, strerror(errno)); + return PAM_SESSION_ERR; + } D1("Chrooting to %s", user->pw_dir); if (chdir(user->pw_dir)) { D1("Unable to chdir to %s: %s", user->pw_dir, strerror(errno)); return PAM_SESSION_ERR; }