static void * try_dlopen(const char *modfn, int *error) { void *dlh; int fd; openpam_log(PAM_LOG_LIBDEBUG, "dlopen(%s)", modfn); if ((fd = open(modfn, O_RDONLY)) < 0) { if (errno != ENOENT) openpam_log(PAM_LOG_ERROR, "%s: %m", modfn); return (NULL); } if (OPENPAM_FEATURE(VERIFY_MODULE_FILE) && openpam_check_desc_owner_perms(modfn, fd) != 0) { close(fd); return (NULL); } if ((dlh = fdlopen(fd, RTLD_NOW)) == NULL) { openpam_log(PAM_LOG_ERROR, "%s: %s", modfn, dlerror()); close(fd); errno = 0; return (NULL); } close(fd); return (dlh); }
/* * Validate a service name. * * Returns a non-zero value if the argument points to a NUL-terminated * string consisting entirely of characters in the POSIX portable filename * character set, excluding the path separator character. */ static int valid_service_name(const char *name) { const char *p; if (OPENPAM_FEATURE(RESTRICT_SERVICE_NAME)) { /* path separator not allowed */ for (p = name; *p != '\0'; ++p) if (!is_pfcs(*p)) return (0); } else { /* path separator allowed */ for (p = name; *p != '\0'; ++p) if (!is_pfcs(*p) && *p != '/') return (0); } return (1); }
/* * Read the specified chains from the specified file. * * Returns 0 if the file exists but does not contain any matching lines. * * Returns -1 and sets errno to ENOENT if the file does not exist. * * Returns -1 and sets errno to some other non-zero value if the file * exists but is unsafe or unreadable, or an I/O error occurs. */ static int openpam_load_file(pam_handle_t *pamh, const char *service, pam_facility_t facility, const char *filename, openpam_style_t style) { FILE *f; int ret, serrno; /* attempt to open the file */ if ((f = fopen(filename, "r")) == NULL) { serrno = errno; openpam_log(errno == ENOENT ? PAM_LOG_DEBUG : PAM_LOG_ERROR, "%s: %m", filename); errno = serrno; RETURNN(-1); } else { openpam_log(PAM_LOG_DEBUG, "found %s", filename); } /* verify type, ownership and permissions */ if (OPENPAM_FEATURE(VERIFY_POLICY_FILE) && openpam_check_desc_owner_perms(filename, fileno(f)) != 0) { /* already logged the cause */ serrno = errno; fclose(f); errno = serrno; RETURNN(-1); } /* parse the file */ ret = openpam_parse_chain(pamh, service, facility, f, filename, style); RETURNN(ret); }
pam_module_t * openpam_dynamic(const char *modname) { pam_module_t *module; char modpath[PATH_MAX]; const char **path, *p; int has_so, has_ver; int dot, len; /* * Simple case: module name contains path separator(s) */ if (strchr(modname, '/') != NULL) { /* * Absolute paths are not allowed if RESTRICT_MODULE_NAME * is in effect (default off). Relative paths are never * allowed. */ if (OPENPAM_FEATURE(RESTRICT_MODULE_NAME) || modname[0] != '/') { openpam_log(PAM_LOG_ERROR, "invalid module name: %s", modname); return (NULL); } return (try_module(modname)); } /* * Check for .so and version sufixes */ p = strchr(modname, '\0'); has_ver = has_so = 0; while (is_digit(*p)) --p; if (*p == '.' && *++p != '\0') { /* found a numeric suffix */ has_ver = 1; /* assume that .so is either present or unneeded */ has_so = 1; } else if (*p == '\0' && p >= modname + sizeof PAM_SOEXT && strcmp(p - sizeof PAM_SOEXT + 1, PAM_SOEXT) == 0) { /* found .so suffix */ has_so = 1; } /* * Complicated case: search for the module in the usual places. */ for (path = openpam_module_path; *path != NULL; ++path) { /* * Assemble the full path, including the version suffix. Take * note of where the suffix begins so we can cut it off later. */ if (has_ver) len = snprintf(modpath, sizeof modpath, "%s/%s%n", *path, modname, &dot); else if (has_so) len = snprintf(modpath, sizeof modpath, "%s/%s%n.%d", *path, modname, &dot, LIB_MAJ); else len = snprintf(modpath, sizeof modpath, "%s/%s%s%n.%d", *path, modname, PAM_SOEXT, &dot, LIB_MAJ); /* check for overflow */ if (len < 0 || (unsigned int)len >= sizeof modpath) { errno = ENOENT; continue; } /* try the versioned path */ if ((module = try_module(modpath)) != NULL) return (module); if (errno == ENOENT && modpath[dot] != '\0') { /* no luck, try the unversioned path */ modpath[dot] = '\0'; if ((module = try_module(modpath)) != NULL) return (module); } } /* :( */ return (NULL); }