/* Return PAM_SUCCESS if the user is NOT in the group. */ static int evaluate_notingroup(pam_handle_t *pamh, const char *user, const char *group) { if (pam_modutil_user_in_group_nam_nam(pamh, user, group) == 0) return PAM_SUCCESS; return PAM_AUTH_ERR; }
static int user_match (pam_handle_t *pamh, char *tok, struct login_info *item) { char *string = item->user->pw_name; struct login_info fake_item; char *at; int rv; if (item->debug) pam_syslog (pamh, LOG_DEBUG, "user_match: tok=%s, item=%s", tok, string); /* * If a token has the magic value "ALL" the match always succeeds. * Otherwise, return YES if the token fully matches the username, if the * token is a group that contains the username, or if the token is the * name of the user's primary group. */ /* Try to split on a pattern (@*[^@]+)(@+.*) */ for (at = tok; *at == '@'; ++at); if ((at = strchr(at, '@')) != NULL) { /* split user@host pattern */ if (item->hostname == NULL) return NO; memcpy (&fake_item, item, sizeof(fake_item)); fake_item.from = item->hostname; fake_item.gai_rv = 0; fake_item.res = NULL; fake_item.from_remote_host = 1; /* hostname should be resolvable */ *at = 0; if (!user_match (pamh, tok, item)) return NO; rv = from_match (pamh, at + 1, &fake_item); if (fake_item.gai_rv == 0 && fake_item.res) freeaddrinfo(fake_item.res); return rv; } else if (tok[0] == '@') { /* netgroup */ const char *hostname = NULL; if (tok[1] == '@') { /* add hostname to netgroup match */ if (item->hostname == NULL) return NO; ++tok; hostname = item->hostname; } return (netgroup_match (pamh, tok + 1, hostname, string, item->debug)); } else if (tok[0] == '(' && tok[strlen(tok) - 1] == ')') return (group_match (pamh, tok, string, item->debug)); else if ((rv=string_match (pamh, tok, string, item->debug)) != NO) /* ALL or exact match */ return rv; else if (item->only_new_group_syntax == NO && pam_modutil_user_in_group_nam_nam (pamh, item->user->pw_name, tok)) /* try group membership */ return YES; return NO; }
static int user_match (pam_handle_t *pamh, char *tok, struct login_info *item) { char *string = item->user->pw_name; struct login_info fake_item; char *at; int rv; if (item->debug) pam_syslog (pamh, LOG_DEBUG, "user_match: tok=%s, item=%s", tok, string); /* * If a token has the magic value "ALL" the match always succeeds. * Otherwise, return YES if the token fully matches the username, if the * token is a group that contains the username, or if the token is the * name of the user's primary group. */ if ((at = strchr(tok + 1, '@')) != 0) { /* split user@host pattern */ if (item->hostname == NULL) return NO; fake_item.from = item->hostname; *at = 0; return (user_match (pamh, tok, item) && from_match (pamh, at + 1, &fake_item)); } else if (tok[0] == '@') { /* netgroup */ const char *hostname = NULL; if (tok[1] == '@') { /* add hostname to netgroup match */ if (item->hostname == NULL) return NO; ++tok; hostname = item->hostname; } return (netgroup_match (pamh, tok + 1, hostname, string, item->debug)); } else if (tok[0] == '(' && tok[strlen(tok) - 1] == ')') return (group_match (pamh, tok, string, item->debug)); else if ((rv=string_match (pamh, tok, string, item->debug)) != NO) /* ALL or exact match */ return rv; else if (item->only_new_group_syntax == NO && pam_modutil_user_in_group_nam_nam (pamh, item->user->pw_name, tok)) /* try group membership */ return YES; return NO; }
static int group_match (pam_handle_t *pamh, const char *tok, const char* usr, int debug) { char grptok[BUFSIZ]; if (debug) pam_syslog (pamh, LOG_DEBUG, "group_match: grp=%s, user=%s", grptok, usr); if (strlen(tok) < 3) return NO; /* token is recieved under the format '(...)' */ memset(grptok, 0, BUFSIZ); strncpy(grptok, tok + 1, strlen(tok) - 2); if (pam_modutil_user_in_group_nam_nam(pamh, usr, grptok)) return YES; return NO; }
static int parse_config_file(pam_handle_t *pamh, const char *uname, uid_t uid, gid_t gid, int ctrl, struct pam_limit_s *pl) { FILE *fil; char buf[LINE_LENGTH]; /* check for the LIMITS_FILE */ if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_DEBUG, "reading settings from '%s'", CONF_FILE); fil = fopen(CONF_FILE, "r"); if (fil == NULL) { pam_syslog (pamh, LOG_WARNING, "cannot read settings from %s: %m", CONF_FILE); return PAM_SERVICE_ERR; } /* start the show */ while (fgets(buf, LINE_LENGTH, fil) != NULL) { char domain[LINE_LENGTH]; char ltype[LINE_LENGTH]; char item[LINE_LENGTH]; char value[LINE_LENGTH]; int i; int rngtype; size_t j; char *tptr,*line; uid_t min_uid = (uid_t)-1, max_uid = (uid_t)-1; line = buf; /* skip the leading white space */ while (*line && isspace(*line)) line++; /* Rip off the comments */ tptr = strchr(line,'#'); if (tptr) *tptr = '\0'; /* Rip off the newline char */ tptr = strchr(line,'\n'); if (tptr) *tptr = '\0'; /* Anything left ? */ if (!strlen(line)) continue; domain[0] = ltype[0] = item[0] = value[0] = '\0'; i = sscanf(line,"%s%s%s%s", domain, ltype, item, value); D(("scanned line[%d]: domain[%s], ltype[%s], item[%s], value[%s]", i, domain, ltype, item, value)); for(j=0; j < strlen(ltype); j++) ltype[j]=tolower(ltype[j]); if ((rngtype=parse_uid_range(pamh, domain, &min_uid, &max_uid)) < 0) { pam_syslog(pamh, LOG_WARNING, "invalid uid range '%s' - skipped", domain); continue; } if (i == 4) { /* a complete line */ for(j=0; j < strlen(item); j++) item[j]=tolower(item[j]); for(j=0; j < strlen(value); j++) value[j]=tolower(value[j]); if (strcmp(uname, domain) == 0) /* this user have a limit */ process_limit(pamh, LIMITS_DEF_USER, ltype, item, value, ctrl, pl); else if (domain[0]=='@') { if (ctrl & PAM_DEBUG_ARG) { pam_syslog(pamh, LOG_DEBUG, "checking if %s is in group %s", uname, domain + 1); } switch(rngtype) { case LIMIT_RANGE_NONE: if (pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1)) process_limit(pamh, LIMITS_DEF_GROUP, ltype, item, value, ctrl, pl); break; case LIMIT_RANGE_ONE: if (pam_modutil_user_in_group_nam_gid(pamh, uname, (gid_t)max_uid)) process_limit(pamh, LIMITS_DEF_GROUP, ltype, item, value, ctrl, pl); break; case LIMIT_RANGE_MM: if (gid > (gid_t)max_uid) break; /* fallthrough */ case LIMIT_RANGE_MIN: if (gid >= (gid_t)min_uid) process_limit(pamh, LIMITS_DEF_GROUP, ltype, item, value, ctrl, pl); } } else if (domain[0]=='%') { if (ctrl & PAM_DEBUG_ARG) { pam_syslog(pamh, LOG_DEBUG, "checking if %s is in group %s", uname, domain + 1); } switch(rngtype) { case LIMIT_RANGE_NONE: if (strcmp(domain,"%") == 0) process_limit(pamh, LIMITS_DEF_ALL, ltype, item, value, ctrl, pl); else if (pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1)) { strcpy(pl->login_group, domain+1); process_limit(pamh, LIMITS_DEF_ALLGROUP, ltype, item, value, ctrl, pl); } break; case LIMIT_RANGE_ONE: if (pam_modutil_user_in_group_nam_gid(pamh, uname, (gid_t)max_uid)) { struct group *grp; grp = pam_modutil_getgrgid(pamh, (gid_t)max_uid); strncpy(pl->login_group, grp->gr_name, sizeof(pl->login_group)); pl->login_group[sizeof(pl->login_group)-1] = '\0'; process_limit(pamh, LIMITS_DEF_ALLGROUP, ltype, item, value, ctrl, pl); } break; case LIMIT_RANGE_MIN: case LIMIT_RANGE_MM: pam_syslog(pamh, LOG_WARNING, "range unsupported for %%group matching - ignored"); } } else { switch(rngtype) { case LIMIT_RANGE_NONE: if (strcmp(domain, "*") == 0) process_limit(pamh, LIMITS_DEF_DEFAULT, ltype, item, value, ctrl, pl); break; case LIMIT_RANGE_ONE: if (uid != max_uid) break; /* fallthrough */ case LIMIT_RANGE_MM: if (uid > max_uid) break; /* fallthrough */ case LIMIT_RANGE_MIN: if (uid >= min_uid) process_limit(pamh, LIMITS_DEF_USER, ltype, item, value, ctrl, pl); } } } else if (i == 2 && ltype[0] == '-') { /* Probably a no-limit line */ if (strcmp(uname, domain) == 0) { if (ctrl & PAM_DEBUG_ARG) { pam_syslog(pamh, LOG_DEBUG, "no limits for '%s'", uname); } } else if (domain[0] == '@') { switch(rngtype) { case LIMIT_RANGE_NONE: if (!pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1)) continue; /* next line */ break; case LIMIT_RANGE_ONE: if (!pam_modutil_user_in_group_nam_gid(pamh, uname, (gid_t)max_uid)) continue; /* next line */ break; case LIMIT_RANGE_MM: if (gid > (gid_t)max_uid) continue; /* next line */ /* fallthrough */ case LIMIT_RANGE_MIN: if (gid < (gid_t)min_uid) continue; /* next line */ } if (ctrl & PAM_DEBUG_ARG) { pam_syslog(pamh, LOG_DEBUG, "no limits for '%s' in group '%s'", uname, domain+1); } } else { switch(rngtype) { case LIMIT_RANGE_NONE: continue; /* next line */ case LIMIT_RANGE_ONE: if (uid != max_uid) continue; /* next line */ break; case LIMIT_RANGE_MM: if (uid > max_uid) continue; /* next line */ /* fallthrough */ case LIMIT_RANGE_MIN: if (uid >= min_uid) break; continue; /* next line */ } if (ctrl & PAM_DEBUG_ARG) { pam_syslog(pamh, LOG_DEBUG, "no limits for '%s'", uname); } } fclose(fil); return PAM_IGNORE; } else { pam_syslog(pamh, LOG_WARNING, "invalid line '%s' - skipped", line); } } fclose(fil); return PAM_SUCCESS; }
/* Counts the number of user logins and check against the limit*/ static int check_logins (pam_handle_t *pamh, const char *name, int limit, int ctrl, struct pam_limit_s *pl) { struct utmp *ut; int count; if (ctrl & PAM_DEBUG_ARG) { pam_syslog(pamh, LOG_DEBUG, "checking logins for '%s' (maximum of %d)", name, limit); } if (limit < 0) return 0; /* no limits imposed */ if (limit == 0) /* maximum 0 logins ? */ { pam_syslog(pamh, LOG_WARNING, "No logins allowed for '%s'", name); return LOGIN_ERR; } setutent(); /* Because there is no definition about when an application actually adds a utmp entry, some applications bizarrely do the utmp call before the have PAM authenticate them to the system: you're logged it, sort of...? Anyway, you can use the "utmp_early" module argument in your PAM config file to make allowances for this sort of problem. (There should be a PAM standard for this, since if a module wants to actually map a username then any early utmp entry will be for the unmapped name = broken.) */ if (ctrl & PAM_UTMP_EARLY) { count = 0; } else { count = 1; } while((ut = getutent())) { #ifdef USER_PROCESS if (ut->ut_type != USER_PROCESS) { continue; } #endif if (ut->UT_USER[0] == '\0') { continue; } if (!pl->flag_numsyslogins) { if (((pl->login_limit_def == LIMITS_DEF_USER) || (pl->login_limit_def == LIMITS_DEF_GROUP) || (pl->login_limit_def == LIMITS_DEF_DEFAULT)) && strncmp(name, ut->UT_USER, sizeof(ut->UT_USER)) != 0) { continue; } if ((pl->login_limit_def == LIMITS_DEF_ALLGROUP) && !pam_modutil_user_in_group_nam_nam(pamh, ut->UT_USER, pl->login_group)) { continue; } } if (++count > limit) { break; } } endutent(); if (count > limit) { if (name) { pam_syslog(pamh, LOG_WARNING, "Too many logins (max %d) for %s", limit, name); } else { pam_syslog(pamh, LOG_WARNING, "Too many system logins (max %d)", limit); } return LOGIN_ERR; } return 0; }
PAM_EXTERN int pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED, int argc, const char **argv) { int retval, i, citem=0, extitem=0, onerr=PAM_SERVICE_ERR, sense=2, quiet=0; const void *void_citemp; const char *citemp; char *ifname=NULL; char aline[256]; char mybuf[256],myval[256]; struct stat fileinfo; FILE *inf; char apply_val[256]; int apply_type; /* Stuff for "extended" items */ struct passwd *userinfo; apply_type=APPLY_TYPE_NULL; memset(apply_val,0,sizeof(apply_val)); for(i=0; i < argc; i++) { { const char *junk; /* option quiet has no value */ if(!strcmp(argv[i],"quiet")) { quiet = 1; continue; } memset(mybuf,'\0',sizeof(mybuf)); memset(myval,'\0',sizeof(myval)); junk = strchr(argv[i], '='); if((junk == NULL) || (junk - argv[i]) >= (int) sizeof(mybuf)) { pam_syslog(pamh,LOG_ERR, "Bad option: \"%s\"", argv[i]); continue; } strncpy(mybuf, argv[i], LESSER(junk - argv[i], (int)sizeof(mybuf) - 1)); strncpy(myval, junk + 1, sizeof(myval) - 1); } if(!strcmp(mybuf,"onerr")) if(!strcmp(myval,"succeed")) onerr = PAM_SUCCESS; else if(!strcmp(myval,"fail")) onerr = PAM_SERVICE_ERR; else { if (ifname) free (ifname); return PAM_SERVICE_ERR; } else if(!strcmp(mybuf,"sense")) if(!strcmp(myval,"allow")) sense=0; else if(!strcmp(myval,"deny")) sense=1; else { if (ifname) free (ifname); return onerr; } else if(!strcmp(mybuf,"file")) { if (ifname) free (ifname); ifname = (char *)malloc(strlen(myval)+1); if (!ifname) return PAM_BUF_ERR; strcpy(ifname,myval); } else if(!strcmp(mybuf,"item")) if(!strcmp(myval,"user")) citem = PAM_USER; else if(!strcmp(myval,"tty")) citem = PAM_TTY; else if(!strcmp(myval,"rhost")) citem = PAM_RHOST; else if(!strcmp(myval,"ruser")) citem = PAM_RUSER; else { /* These items are related to the user, but are not directly gettable with pam_get_item */ citem = PAM_USER; if(!strcmp(myval,"group")) extitem = EI_GROUP; else if(!strcmp(myval,"shell")) extitem = EI_SHELL; else citem = 0; } else if(!strcmp(mybuf,"apply")) { apply_type=APPLY_TYPE_NONE; memset(apply_val,'\0',sizeof(apply_val)); if (myval[0]=='@') { apply_type=APPLY_TYPE_GROUP; strncpy(apply_val,myval+1,sizeof(apply_val)-1); } else { apply_type=APPLY_TYPE_USER; strncpy(apply_val,myval,sizeof(apply_val)-1); } } else { free(ifname); pam_syslog(pamh,LOG_ERR, "Unknown option: %s",mybuf); return onerr; } } if(!citem) { pam_syslog(pamh,LOG_ERR, "Unknown item or item not specified"); free(ifname); return onerr; } else if(!ifname) { pam_syslog(pamh,LOG_ERR, "List filename not specified"); return onerr; } else if(sense == 2) { pam_syslog(pamh,LOG_ERR, "Unknown sense or sense not specified"); free(ifname); return onerr; } else if( (apply_type==APPLY_TYPE_NONE) || ((apply_type!=APPLY_TYPE_NULL) && (*apply_val=='\0')) ) { pam_syslog(pamh,LOG_ERR, "Invalid usage for apply= parameter"); free (ifname); return onerr; } /* Check if it makes sense to use the apply= parameter */ if (apply_type != APPLY_TYPE_NULL) { if((citem==PAM_USER) || (citem==PAM_RUSER)) { pam_syslog(pamh,LOG_WARNING, "Non-sense use for apply= parameter"); apply_type=APPLY_TYPE_NULL; } if(extitem && (extitem==EI_GROUP)) { pam_syslog(pamh,LOG_WARNING, "Non-sense use for apply= parameter"); apply_type=APPLY_TYPE_NULL; } } /* Short-circuit - test if this session apply for this user */ { const char *user_name; int rval; rval=pam_get_user(pamh,&user_name,NULL); if((rval==PAM_SUCCESS) && user_name && user_name[0]) { /* Got it ? Valid ? */ if(apply_type==APPLY_TYPE_USER) { if(strcmp(user_name, apply_val)) { /* Does not apply to this user */ #ifdef PAM_DEBUG pam_syslog(pamh,LOG_DEBUG, "don't apply: apply=%s, user=%s", apply_val,user_name); #endif /* PAM_DEBUG */ free(ifname); return PAM_IGNORE; } } else if(apply_type==APPLY_TYPE_GROUP) { if(!pam_modutil_user_in_group_nam_nam(pamh,user_name,apply_val)) { /* Not a member of apply= group */ #ifdef PAM_DEBUG pam_syslog(pamh,LOG_DEBUG, "don't apply: %s not a member of group %s", user_name,apply_val); #endif /* PAM_DEBUG */ free(ifname); return PAM_IGNORE; } } } } retval = pam_get_item(pamh,citem,&void_citemp); citemp = void_citemp; if(retval != PAM_SUCCESS) { free(ifname); return onerr; } if((citem == PAM_USER) && !citemp) { retval = pam_get_user(pamh,&citemp,NULL); if (retval != PAM_SUCCESS || !citemp) { free(ifname); return PAM_SERVICE_ERR; } } if((citem == PAM_TTY) && citemp) { /* Normalize the TTY name. */ if(strncmp(citemp, "/dev/", 5) == 0) { citemp += 5; } } if(!citemp || (strlen(citemp) == 0)) { free(ifname); /* The item was NULL - we are sure not to match */ return sense?PAM_SUCCESS:PAM_AUTH_ERR; } if(extitem) { switch(extitem) { case EI_GROUP: /* Just ignore, call pam_modutil_in_group... later */ break; case EI_SHELL: /* Assume that we have already gotten PAM_USER in pam_get_item() - a valid assumption since citem gets set to PAM_USER in the extitem switch */ userinfo = pam_modutil_getpwnam(pamh, citemp); if (userinfo == NULL) { pam_syslog(pamh,LOG_ERR, "getpwnam(%s) failed", citemp); free(ifname); return onerr; } citemp = userinfo->pw_shell; break; default: pam_syslog(pamh,LOG_ERR, "Internal weirdness, unknown extended item %d", extitem); free(ifname); return onerr; } } #ifdef PAM_DEBUG pam_syslog(pamh,LOG_INFO, "Got file = %s, item = %d, value = %s, sense = %d", ifname, citem, citemp, sense); #endif if(lstat(ifname,&fileinfo)) { if(!quiet) pam_syslog(pamh,LOG_ERR, "Couldn't open %s",ifname); free(ifname); return onerr; } if((fileinfo.st_mode & S_IWOTH) || !S_ISREG(fileinfo.st_mode)) { /* If the file is world writable or is not a normal file, return error */ pam_syslog(pamh,LOG_ERR, "%s is either world writable or not a normal file", ifname); free(ifname); return PAM_AUTH_ERR; } inf = fopen(ifname,"r"); if(inf == NULL) { /* Check that we opened it successfully */ if (onerr == PAM_SERVICE_ERR) { /* Only report if it's an error... */ pam_syslog(pamh,LOG_ERR, "Error opening %s", ifname); } free(ifname); return onerr; } /* There should be no more errors from here on */ retval=PAM_AUTH_ERR; /* This loop assumes that PAM_SUCCESS == 0 and PAM_AUTH_ERR != 0 */ #ifdef PAM_DEBUG assert(PAM_SUCCESS == 0); assert(PAM_AUTH_ERR != 0); #endif while((fgets(aline,sizeof(aline),inf) != NULL) && retval) { char *a = aline; if(strlen(aline) == 0) continue; if(aline[strlen(aline) - 1] == '\n') aline[strlen(aline) - 1] = '\0'; if(strlen(aline) == 0) continue; if(aline[strlen(aline) - 1] == '\r') aline[strlen(aline) - 1] = '\0'; if(citem == PAM_TTY) { if(strncmp(a, "/dev/", 5) == 0) a += 5; } if (extitem == EI_GROUP) { retval = !pam_modutil_user_in_group_nam_nam(pamh, citemp, aline); } else { retval = strcmp(a, citemp); } } fclose(inf); free(ifname); if ((sense && retval) || (!sense && !retval)) { #ifdef PAM_DEBUG pam_syslog(pamh,LOG_INFO, "Returning PAM_SUCCESS, retval = %d", retval); #endif return PAM_SUCCESS; } else { const void *service; const char *user_name; #ifdef PAM_DEBUG pam_syslog(pamh,LOG_INFO, "Returning PAM_AUTH_ERR, retval = %d", retval); #endif (void) pam_get_item(pamh, PAM_SERVICE, &service); (void) pam_get_user(pamh, &user_name, NULL); if (!quiet) pam_syslog (pamh, LOG_ALERT, "Refused user %s for service %s", user_name, (const char *)service); return PAM_AUTH_ERR; } }
static int parse_config_file(pam_handle_t *pamh, const char *uname, int ctrl, struct pam_limit_s *pl) { FILE *fil; char buf[LINE_LENGTH]; #define CONF_FILE (pl->conf_file[0])?pl->conf_file:LIMITS_FILE /* check for the LIMITS_FILE */ if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_DEBUG, "reading settings from '%s'", CONF_FILE); fil = fopen(CONF_FILE, "r"); if (fil == NULL) { pam_syslog (pamh, LOG_WARNING, "cannot read settings from %s: %m", CONF_FILE); return PAM_SERVICE_ERR; } #undef CONF_FILE /* init things */ memset(buf, 0, sizeof(buf)); /* start the show */ while (fgets(buf, LINE_LENGTH, fil) != NULL) { char domain[LINE_LENGTH]; char ltype[LINE_LENGTH]; char item[LINE_LENGTH]; char value[LINE_LENGTH]; int i; size_t j; char *tptr; tptr = buf; /* skip the leading white space */ while (*tptr && isspace(*tptr)) tptr++; strncpy(buf, tptr, sizeof(buf)-1); buf[sizeof(buf)-1] = '\0'; /* Rip off the comments */ tptr = strchr(buf,'#'); if (tptr) *tptr = '\0'; /* Rip off the newline char */ tptr = strchr(buf,'\n'); if (tptr) *tptr = '\0'; /* Anything left ? */ if (!strlen(buf)) { memset(buf, 0, sizeof(buf)); continue; } memset(domain, 0, sizeof(domain)); memset(ltype, 0, sizeof(ltype)); memset(item, 0, sizeof(item)); memset(value, 0, sizeof(value)); i = sscanf(buf,"%s%s%s%s", domain, ltype, item, value); D(("scanned line[%d]: domain[%s], ltype[%s], item[%s], value[%s]", i, domain, ltype, item, value)); for(j=0; j < strlen(ltype); j++) ltype[j]=tolower(ltype[j]); for(j=0; j < strlen(item); j++) item[j]=tolower(item[j]); for(j=0; j < strlen(value); j++) value[j]=tolower(value[j]); if (i == 4) { /* a complete line */ if (strcmp(uname, domain) == 0) /* this user have a limit */ process_limit(pamh, LIMITS_DEF_USER, ltype, item, value, ctrl, pl); else if (domain[0]=='@') { if (ctrl & PAM_DEBUG_ARG) { pam_syslog(pamh, LOG_DEBUG, "checking if %s is in group %s", uname, domain + 1); } if (pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1)) process_limit(pamh, LIMITS_DEF_GROUP, ltype, item, value, ctrl, pl); } else if (domain[0]=='%') { if (ctrl & PAM_DEBUG_ARG) { pam_syslog(pamh, LOG_DEBUG, "checking if %s is in group %s", uname, domain + 1); } if (strcmp(domain,"%") == 0) process_limit(pamh, LIMITS_DEF_ALL, ltype, item, value, ctrl, pl); else if (pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1)) { strcpy(pl->login_group, domain+1); process_limit(pamh, LIMITS_DEF_ALLGROUP, ltype, item, value, ctrl, pl); } } else if (strcmp(domain, "*") == 0) process_limit(pamh, LIMITS_DEF_DEFAULT, ltype, item, value, ctrl, pl); } else if (i == 2 && ltype[0] == '-') { /* Probably a no-limit line */ if (strcmp(uname, domain) == 0) { if (ctrl & PAM_DEBUG_ARG) { pam_syslog(pamh, LOG_DEBUG, "no limits for '%s'", uname); } fclose(fil); return PAM_IGNORE; } else if (domain[0] == '@' && pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1)) { if (ctrl & PAM_DEBUG_ARG) { pam_syslog(pamh, LOG_DEBUG, "no limits for '%s' in group '%s'", uname, domain+1); } fclose(fil); return PAM_IGNORE; } } else { pam_syslog(pamh, LOG_WARNING, "invalid line '%s' - skipped", buf); } } fclose(fil); return PAM_SUCCESS; }
PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) { struct options *opts; FILE *fh; char *username; /* username requesting access */ char *rhost; /* remote host */ char *srv; /* PAM service we're running as */ char buf[LINE_LENGTH]; int retval, action; int is_v6 = 0; struct locations *geo; unsigned char gi_type; GeoIP *gi = NULL; #ifdef HAVE_GEOIP_010408 GeoIP *gi6 = NULL; int is_city6_db = 0; #endif GeoIPRecord *rec = NULL; opts = malloc(sizeof(struct options)); if (opts == NULL) { pam_syslog(pamh, LOG_CRIT, "malloc error 'opts': %m"); return PAM_SERVICE_ERR; } opts->charset = GEOIP_CHARSET_UTF8; opts->debug = 0; opts->action = PAM_PERM_DENIED; opts->system_file = NULL; opts->service_file = NULL; opts->by_service = 0; opts->geoip_db = NULL; #ifdef HAVE_GEOIP_010408 opts->use_v6 = 0; opts->v6_first = 0; opts->geoip6_db = NULL; #endif opts->is_city_db = 0; geo = malloc(sizeof(struct locations)); if (geo == NULL) { pam_syslog(pamh, LOG_CRIT, "malloc error 'geo': %m"); free_opts(opts); return PAM_SERVICE_ERR; } geo->country = NULL; geo->city = NULL; geo->next = NULL; _parse_args(pamh, argc, argv, opts); if (opts->system_file == NULL) opts->system_file = strdup(SYSTEM_FILE); if (opts->system_file == NULL) { pam_syslog(pamh, LOG_CRIT, "malloc error 'opts->system_file': %m"); free_opts(opts); return PAM_SERVICE_ERR; } if (opts->geoip_db == NULL) opts->geoip_db = strdup(GEOIPDB_FILE); if (opts->geoip_db == NULL) { pam_syslog(pamh, LOG_CRIT, "malloc error 'opts->geoip_db': %m"); free_opts(opts); return PAM_SERVICE_ERR; } #ifdef HAVE_GEOIP_010408 if (opts->geoip6_db == NULL) opts->geoip6_db = strdup(GEOIP6DB_FILE); if (opts->geoip6_db == NULL) { pam_syslog(pamh, LOG_CRIT, "malloc error 'opts->geoip6_db': %m"); free_opts(opts); return PAM_SERVICE_ERR; } #endif retval = pam_get_item(pamh, PAM_USER, (void*) &username); if (username == NULL || retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_CRIT, "error recovering username"); free_opts(opts); free_locations(geo); return PAM_SERVICE_ERR; } retval = pam_get_item(pamh, PAM_RHOST, (void*) &rhost); if (retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_CRIT, "error fetching rhost"); free_opts(opts); free_locations(geo); return PAM_SERVICE_ERR; } if (rhost == NULL) { pam_syslog(pamh, LOG_INFO, "rhost is NULL, allowing"); free_opts(opts); free_locations(geo); return PAM_SUCCESS; } retval = pam_get_item(pamh, PAM_SERVICE, (void*) &srv); if (srv == NULL || retval != PAM_SUCCESS ) { pam_syslog(pamh, LOG_CRIT, "error requesting service name"); free_opts(opts); free_locations(geo); return PAM_SERVICE_ERR; } opts->service_file = malloc(PATH_MAX); if (opts->service_file == NULL) { pam_syslog(pamh, LOG_CRIT, "malloc error 'service_file': %m"); free_opts(opts); free_locations(geo); return PAM_SERVICE_ERR; } if (snprintf(opts->service_file, PATH_MAX-1, SERVICE_FILE, srv) < 0) { pam_syslog(pamh, LOG_CRIT, "snprintf error 'service_file'"); free_opts(opts); free_locations(geo); return PAM_SERVICE_ERR; } gi = GeoIP_open(opts->geoip_db, GEOIP_INDEX_CACHE); if (gi == NULL) { pam_syslog(pamh, LOG_CRIT, "failed to open geoip db (%s): %m", opts->geoip_db); free_opts(opts); free_locations(geo); return PAM_SERVICE_ERR; } gi_type = GeoIP_database_edition(gi); if (opts->debug) pam_syslog(pamh, LOG_DEBUG, "GeoIP edition: %d", gi_type); switch (gi_type) { case GEOIP_COUNTRY_EDITION: if (opts->debug) pam_syslog(pamh, LOG_DEBUG, "GeoIP v4 edition: country"); opts->is_city_db = 0; break; case GEOIP_CITY_EDITION_REV0: if (opts->debug) pam_syslog(pamh, LOG_DEBUG, "GeoIP v4 edition: city rev0"); opts->is_city_db = 1; break; case GEOIP_CITY_EDITION_REV1: if (opts->debug) pam_syslog(pamh, LOG_DEBUG, "GeoIP v4 edition: city rev1"); opts->is_city_db = 1; break; default: pam_syslog(pamh, LOG_CRIT, "invalid GeoIP DB type `%d' found", gi_type); GeoIP_delete(gi); free_opts(opts); free_locations(geo); return PAM_SERVICE_ERR; } GeoIP_set_charset(gi, opts->charset); if (opts->debug) pam_syslog(pamh, LOG_DEBUG, "GeoIP DB is City: %s", opts->is_city_db ? "yes" : "no"); #ifdef HAVE_GEOIP_010408 if (opts->use_v6 != 0) { gi6 = GeoIP_open(opts->geoip6_db, GEOIP_INDEX_CACHE); if (gi6 == NULL) { pam_syslog(pamh, LOG_CRIT, "failed to open geoip6 db (%s): %m", opts->geoip6_db); GeoIP_delete(gi); free_opts(opts); free_locations(geo); return PAM_SERVICE_ERR; } gi_type = GeoIP_database_edition(gi6); switch (gi_type) { case GEOIP_COUNTRY_EDITION_V6: if (opts->debug) pam_syslog(pamh, LOG_DEBUG, "GeoIP v6 edition: country"); is_city6_db = 0; break; case GEOIP_CITY_EDITION_REV0_V6: if (opts->debug) pam_syslog(pamh, LOG_DEBUG, "GeoIP v6 edition: city rev0"); is_city6_db = 1; break; case GEOIP_CITY_EDITION_REV1_V6: if (opts->debug) pam_syslog(pamh, LOG_DEBUG, "GeoIP v6 edition: city rev1"); is_city6_db = 1; break; default: pam_syslog(pamh, LOG_CRIT, "invalid GeoIP DB type `%d' found", gi_type); GeoIP_delete(gi); GeoIP_delete(gi6); free_opts(opts); free_locations(geo); return PAM_SERVICE_ERR; } if (opts->debug) pam_syslog(pamh, LOG_DEBUG, "GeoIP DB is City v6: %s", is_city6_db ? "yes" : "no"); GeoIP_set_charset(gi6, opts->charset); if (opts->is_city_db != is_city6_db) { pam_syslog(pamh, LOG_CRIT, "IPv4 DB type is not the same as IPv6 (not both Country edition or both City edition)"); GeoIP_delete(gi); GeoIP_delete(gi6); free_opts(opts); free_locations(geo); return PAM_SERVICE_ERR; } if (opts->v6_first != 0) { rec = GeoIP_record_by_name_v6(gi6, rhost); if (rec == NULL) { if (opts->debug) pam_syslog(pamh, LOG_DEBUG, "no IPv6 record for %s, trying IPv4", rhost); rec = GeoIP_record_by_name(gi, rhost); } else is_v6 = 1; } else { rec = GeoIP_record_by_name(gi, rhost); if (rec == NULL) { if (opts->debug) pam_syslog(pamh, LOG_DEBUG, "no IPv4 record for %s, trying IPv6", rhost); rec = GeoIP_record_by_name_v6(gi6, rhost); if (rec != NULL) is_v6 = 1; } } } else #endif /* HAVE_GEOIP_010408 */ rec = GeoIP_record_by_name(gi, rhost); if (rec == NULL) { pam_syslog(pamh, LOG_INFO, "no record for %s, setting GeoIP to 'UNKNOWN,*'", rhost); geo->city = strdup("*"); geo->country = strdup("UNKNOWN"); if (geo->city == NULL || geo->country == NULL) { pam_syslog(pamh, LOG_CRIT, "malloc error 'geo->{city,country}': %m"); GeoIP_delete(gi); #ifdef HAVE_GEOIP_010408 GeoIP_delete(gi6); #endif free_opts(opts); free_locations(geo); return PAM_SERVICE_ERR; } } else { if (rec->city == NULL || opts->is_city_db == 0) geo->city = strdup("*"); else geo->city = strdup(rec->city); if (rec->country_code == NULL) geo->country = strdup("UNKNOWN"); else geo->country = strdup(rec->country_code); if (geo->city == NULL || geo->country == NULL) { pam_syslog(pamh, LOG_CRIT, "malloc error 'geo->{city,country}': %m"); GeoIP_delete(gi); #ifdef HAVE_GEOIP_010408 GeoIP_delete(gi6); #endif free_opts(opts); free_locations(geo); return PAM_SERVICE_ERR; } if (opts->is_city_db) { geo->latitude = rec->latitude; geo->longitude = rec->longitude; } } if (opts->debug) pam_syslog(pamh, LOG_DEBUG, "GeoIP record for %s: %s,%s", rhost, geo->country, geo->city); if (opts->debug && strcmp(geo->country, "UNKNOWN") != 0 && opts->is_city_db) pam_syslog(pamh, LOG_DEBUG, "GeoIP coordinates for %s: %f,%f", rhost, geo->latitude, geo->longitude); if ((fh = fopen(opts->service_file, "r")) != NULL) { opts->by_service = 1; if (opts->debug) pam_syslog(pamh, LOG_DEBUG, "using services file %s", opts->service_file); } else { if ((fh = fopen(opts->system_file, "r")) == NULL) { pam_syslog(pamh, LOG_CRIT, "error opening %s: %m", opts->system_file); #ifdef HAVE_GEOIP_010408 if (gi6) GeoIP_delete(gi6); #endif if (gi) GeoIP_delete(gi); if (rec) GeoIPRecord_delete(rec); free_opts(opts); return PAM_SERVICE_ERR; } } action = opts->action; char location[LINE_LENGTH]; while (fgets(buf, LINE_LENGTH, fh) != NULL) { char *line, *ptr; char domain[LINE_LENGTH], service[LINE_LENGTH]; action = opts->action; line = buf; /* skip the leading white space */ while (*line && isspace(*line)) line++; /* Rip off the comments */ ptr = strchr(line,'#'); if (ptr) *ptr = '\0'; /* Rip off the newline char */ ptr = strchr(line,'\n'); if (ptr) *ptr = '\0'; /* Anything left ? */ if (!strlen(line)) continue; if (opts->by_service) action = parse_line_srv(pamh, line, domain, location); else action = parse_line_sys(pamh, line, domain, service, location); if (action < 0) { /* parsing failed */ action = opts->action; continue; } if (!opts->by_service) { if (!check_service(pamh, service, srv)) continue; } if ((strcmp(domain, "*") == 0) || (strcmp(username, domain) == 0)) { if (check_location(pamh, opts, location, geo)) break; } else if (domain[0] == '@') { if (pam_modutil_user_in_group_nam_nam(pamh, username, domain+1)) { if (check_location(pamh, opts, location, geo)) break; } } } fclose(fh); if (gi) GeoIP_delete(gi); #ifdef HAVE_GEOIP_010408 if (gi6) GeoIP_delete(gi6); #endif if (rec) GeoIPRecord_delete(rec); free_locations(geo); switch (action) { case PAM_SUCCESS: pam_syslog(pamh, LOG_DEBUG, "location %s allowed for user %s from %s (IPv%d)", location, username, rhost, is_v6 ? 6 : 4); break; case PAM_PERM_DENIED: pam_syslog(pamh, LOG_DEBUG, "location %s denied for user %s from %s (IPv%d)", location, username, rhost, is_v6 ? 6 : 4); break; case PAM_IGNORE: pam_syslog(pamh, LOG_DEBUG, "location %s ignored for user %s from %s (IPv%d)", location, username, rhost, is_v6 ? 6 : 4); break; default: /* should not happen */ pam_syslog(pamh, LOG_DEBUG, "location status: %d, IPv%d", action, is_v6 ? 6 : 4); break; }; free_opts(opts); return action; }