| /* |
| * regex.c |
| * |
| * regex idmapping functions. |
| * |
| * Copyright (c) 2017-2020 Stefan Walter <stefan.walter@inf.ethz.ch>. |
| * Copyright (c) 2008 David H?rdeman <david@hardeman.nu>. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the University nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <pwd.h> |
| #include <grp.h> |
| #include <errno.h> |
| #include <err.h> |
| #include <regex.h> |
| |
| #include "nfsidmap.h" |
| #include "nfsidmap_plugin.h" |
| |
| #define CONFIG_GET_STRING nfsidmap_config_get |
| extern const char *nfsidmap_config_get(const char *, const char *); |
| |
| #define MAX_MATCHES 100 |
| |
| regex_t group_re; |
| regex_t user_re; |
| regex_t gpx_re; |
| int use_gpx; |
| const char * group_prefix; |
| const char * group_name_prefix; |
| const char * group_suffix; |
| const char * user_prefix; |
| const char * user_suffix; |
| const char * group_map_file; |
| const char * group_map_section; |
| char empty = '\0'; |
| size_t group_name_prefix_length; |
| |
| struct pwbuf { |
| struct passwd pwbuf; |
| char buf[1]; |
| }; |
| |
| struct grbuf { |
| struct group grbuf; |
| char buf[1]; |
| }; |
| |
| static char *get_default_domain(void) |
| { |
| static char default_domain[NFS4_MAX_DOMAIN_LEN] = ""; |
| if (default_domain[0] == 0) { |
| nfs4_get_default_domain(NULL, default_domain, NFS4_MAX_DOMAIN_LEN); |
| } |
| return default_domain; |
| } |
| |
| /* |
| * Regexp Translation Methods |
| * |
| */ |
| |
| static struct passwd *regex_getpwnam(const char *name, const char *UNUSED(domain), |
| int *err_p) |
| { |
| struct passwd *pw; |
| struct pwbuf *buf; |
| size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX); |
| char *localname; |
| size_t namelen; |
| int err; |
| int status; |
| int index; |
| regmatch_t matches[MAX_MATCHES]; |
| |
| buf = malloc(sizeof(*buf) + buflen); |
| if (!buf) { |
| err = ENOMEM; |
| goto err; |
| } |
| |
| status = regexec(&user_re, name, MAX_MATCHES, matches, 0); |
| if (status) { |
| IDMAP_LOG(4, ("regexp_getpwnam: user '%s' did not match regex", name)); |
| err = ENOENT; |
| goto err_free_buf; |
| } |
| |
| for (index = 1; index < MAX_MATCHES ; index++) |
| { |
| if (matches[index].rm_so >= 0) |
| break; |
| } |
| |
| if (index == MAX_MATCHES) { |
| IDMAP_LOG(4, ("regexp_getpwnam: user '%s' did not match regex", name)); |
| err = ENOENT; |
| goto err_free_buf; |
| } |
| |
| namelen = matches[index].rm_eo - matches[index].rm_so; |
| localname= malloc(namelen + 1); |
| if (!localname) |
| { |
| err = ENOMEM; |
| goto err_free_buf; |
| } |
| strncpy(localname, name+matches[index].rm_so, namelen); |
| localname[namelen] = '\0'; |
| |
| again: |
| err = getpwnam_r(localname, &buf->pwbuf, buf->buf, buflen, &pw); |
| |
| if (err == EINTR) |
| goto again; |
| |
| if (!pw) { |
| if (err == 0) |
| err = ENOENT; |
| |
| IDMAP_LOG(4, ("regex_getpwnam: local user '%s' for '%s' not found", |
| localname, name)); |
| |
| goto err_free_name; |
| } |
| |
| IDMAP_LOG(4, ("regexp_getpwnam: name '%s' mapped to '%s'", |
| name, localname)); |
| |
| free(localname); |
| *err_p = 0; |
| return pw; |
| |
| err_free_name: |
| free(localname); |
| err_free_buf: |
| free(buf); |
| err: |
| *err_p = err; |
| return NULL; |
| } |
| |
| static struct group *regex_getgrnam(const char *name, const char *UNUSED(domain), |
| int *err_p) |
| { |
| struct group *gr; |
| struct grbuf *buf; |
| size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX); |
| char *localgroup; |
| char *groupname; |
| size_t namelen; |
| int err = 0; |
| int index; |
| int status; |
| regmatch_t matches[MAX_MATCHES]; |
| |
| buf = malloc(sizeof(*buf) + buflen); |
| if (!buf) { |
| err = ENOMEM; |
| goto err; |
| } |
| |
| status = regexec(&group_re, name, MAX_MATCHES, matches, 0); |
| if (status) { |
| IDMAP_LOG(4, ("regexp_getgrnam: group '%s' did not match regex", name)); |
| err = ENOENT; |
| goto err_free_buf; |
| } |
| |
| for (index = 1; index < MAX_MATCHES ; index++) |
| { |
| if (matches[index].rm_so >= 0) |
| break; |
| } |
| |
| if (index == MAX_MATCHES) { |
| IDMAP_LOG(4, ("regexp_getgrnam: group '%s' did not match regex", name)); |
| err = ENOENT; |
| goto err_free_buf; |
| } |
| |
| namelen = matches[index].rm_eo - matches[index].rm_so; |
| localgroup = malloc(namelen + 1); |
| if (!localgroup) |
| { |
| err = ENOMEM; |
| goto err_free_buf; |
| } |
| strncpy(localgroup, name+matches[index].rm_so, namelen); |
| localgroup[namelen] = '\0'; |
| |
| IDMAP_LOG(4, ("regexp_getgrnam: group '%s' after match of regex", localgroup)); |
| |
| groupname = localgroup; |
| if (group_name_prefix_length && ! strncmp(group_name_prefix, localgroup, group_name_prefix_length)) |
| { |
| err = 1; |
| if (use_gpx) |
| err = regexec(&gpx_re, localgroup, 0, NULL, 0); |
| |
| if (err) |
| { |
| IDMAP_LOG(4, ("regexp_getgrnam: removing prefix '%s' (%d long) from group '%s'", group_name_prefix, group_name_prefix_length, localgroup)); |
| groupname += group_name_prefix_length; |
| } |
| else |
| { |
| IDMAP_LOG(4, ("regexp_getgrnam: not removing prefix from group '%s'", localgroup)); |
| } |
| } |
| |
| IDMAP_LOG(4, ("regexp_getgrnam: will use '%s'", groupname)); |
| |
| again: |
| err = getgrnam_r(groupname, &buf->grbuf, buf->buf, buflen, &gr); |
| |
| if (err == EINTR) |
| goto again; |
| |
| if (!gr) { |
| if (err == 0) |
| err = ENOENT; |
| |
| IDMAP_LOG(4, ("regex_getgrnam: local group '%s' for '%s' not found", groupname, name)); |
| |
| goto err_free_name; |
| } |
| |
| IDMAP_LOG(4, ("regex_getgrnam: group '%s' mapped to '%s'", name, groupname)); |
| |
| free(localgroup); |
| |
| *err_p = 0; |
| return gr; |
| |
| err_free_name: |
| free(localgroup); |
| err_free_buf: |
| free(buf); |
| err: |
| *err_p = err; |
| return NULL; |
| } |
| |
| static int regex_gss_princ_to_ids(char *secname, char *princ, |
| uid_t *uid, uid_t *gid, |
| extra_mapping_params **UNUSED(ex)) |
| { |
| struct passwd *pw; |
| int err; |
| |
| /* XXX: Is this necessary? */ |
| if (strcmp(secname, "krb5") != 0 && strcmp(secname, "spkm3") != 0) |
| return -EINVAL; |
| |
| pw = regex_getpwnam(princ, NULL, &err); |
| |
| if (pw) { |
| *uid = pw->pw_uid; |
| *gid = pw->pw_gid; |
| free(pw); |
| } |
| |
| return -err; |
| } |
| |
| static int regex_gss_princ_to_grouplist(char *secname, char *princ, |
| gid_t *groups, int *ngroups, |
| extra_mapping_params **UNUSED(ex)) |
| { |
| struct passwd *pw; |
| int err; |
| |
| /* XXX: Is this necessary? */ |
| if (strcmp(secname, "krb5") != 0 && strcmp(secname, "spkm3") != 0) |
| return -EINVAL; |
| |
| pw = regex_getpwnam(princ, NULL, &err); |
| |
| if (pw) { |
| if (getgrouplist(pw->pw_name, pw->pw_gid, groups, ngroups) < 0) |
| err = -ERANGE; |
| free(pw); |
| } |
| |
| return -err; |
| } |
| |
| static int regex_name_to_uid(char *name, uid_t *uid) |
| { |
| struct passwd *pw; |
| int err; |
| |
| pw = regex_getpwnam(name, NULL, &err); |
| |
| if (pw) { |
| *uid = pw->pw_uid; |
| free(pw); |
| } |
| |
| return -err; |
| } |
| |
| static int regex_name_to_gid(char *name, gid_t *gid) |
| { |
| struct group *gr; |
| int err; |
| |
| gr = regex_getgrnam(name, NULL, &err); |
| |
| if (gr) { |
| *gid = gr->gr_gid; |
| free(gr); |
| } |
| |
| return -err; |
| } |
| |
| static int write_name(char *dest, char *localname, const char* name_prefix, const char *prefix, const char *suffix, size_t len) |
| { |
| if (strlen(localname) + strlen(name_prefix) + strlen(prefix) + strlen(suffix) + 1 > len) { |
| return -ENOMEM; /* XXX: Is there an -ETOOLONG? */ |
| } |
| strcpy(dest, prefix); |
| strcat(dest, name_prefix); |
| strcat(dest, localname); |
| strcat(dest, suffix); |
| |
| IDMAP_LOG(4, ("write_name: will use '%s'", dest)); |
| |
| return 0; |
| } |
| |
| static int regex_uid_to_name(uid_t uid, char *domain, char *name, size_t len) |
| { |
| struct passwd *pw = NULL; |
| struct passwd pwbuf; |
| char *buf; |
| size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX); |
| int err = -ENOMEM; |
| |
| buf = malloc(buflen); |
| if (!buf) |
| goto out; |
| if (domain == NULL) |
| domain = get_default_domain(); |
| err = -getpwuid_r(uid, &pwbuf, buf, buflen, &pw); |
| if (pw == NULL) |
| err = -ENOENT; |
| if (err) |
| goto out_buf; |
| err = write_name(name, pw->pw_name, &empty, user_prefix, user_suffix, len); |
| out_buf: |
| free(buf); |
| out: |
| return err; |
| } |
| |
| static int regex_gid_to_name(gid_t gid, char *UNUSED(domain), char *name, size_t len) |
| { |
| struct group *gr = NULL; |
| struct group grbuf; |
| char *buf; |
| const char *name_prefix; |
| size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX); |
| int err; |
| char * groupname = NULL; |
| |
| do { |
| err = -ENOMEM; |
| buf = malloc(buflen); |
| if (!buf) |
| goto out; |
| err = -getgrgid_r(gid, &grbuf, buf, buflen, &gr); |
| if (gr == NULL && !err) |
| err = -ENOENT; |
| if (err == -ERANGE) { |
| buflen *= 2; |
| free(buf); |
| } |
| } while (err == -ERANGE); |
| |
| if (err) |
| goto out_buf; |
| |
| groupname = gr->gr_name; |
| name_prefix = group_name_prefix; |
| if (group_name_prefix_length) |
| { |
| if(! strncmp(group_name_prefix, groupname, group_name_prefix_length)) |
| { |
| name_prefix = ∅ |
| } |
| else if (use_gpx) |
| { |
| err = regexec(&gpx_re, groupname, 0, NULL, 0); |
| if (!err) |
| { |
| IDMAP_LOG(4, ("regex_gid_to_name: not adding prefix to group '%s'", groupname)); |
| name_prefix = ∅ |
| } |
| } |
| } |
| |
| err = write_name(name, groupname, name_prefix, group_prefix, group_suffix, len); |
| |
| out_buf: |
| free(buf); |
| out: |
| return err; |
| } |
| |
| static int regex_init(void) { |
| const char *string; |
| int status; |
| |
| |
| string = CONFIG_GET_STRING("Regex", "User-Regex"); |
| if (!string) |
| { |
| warnx("regex_init: regex for user mapping missing"); |
| goto error1; |
| } |
| |
| status = regcomp(&user_re, string, REG_EXTENDED|REG_ICASE); |
| if (status) |
| { |
| warnx("regex_init: compiling regex for user mapping failed with status %u", status); |
| goto error1; |
| } |
| |
| string = CONFIG_GET_STRING("Regex", "Group-Regex"); |
| if (!string) |
| { |
| warnx("regex_init: regex for group mapping missing"); |
| goto error2; |
| } |
| |
| status = regcomp(&group_re, string, REG_EXTENDED|REG_ICASE); |
| if (status) |
| { |
| warnx("regex_init: compiling regex for group mapping failed with status %u", status); |
| goto error2; |
| } |
| |
| group_name_prefix = CONFIG_GET_STRING("Regex", "Group-Name-Prefix"); |
| if (!group_name_prefix) |
| { |
| group_name_prefix = ∅ |
| } |
| group_name_prefix_length = strlen(group_name_prefix); |
| |
| user_prefix = CONFIG_GET_STRING("Regex", "Prepend-Before-User"); |
| if (!user_prefix) |
| { |
| user_prefix = ∅ |
| } |
| |
| user_suffix = CONFIG_GET_STRING("Regex", "Append-After-User"); |
| if (!user_suffix) |
| { |
| user_suffix = ∅ |
| } |
| |
| group_prefix = CONFIG_GET_STRING("Regex", "Prepend-Before-Group"); |
| if (!group_prefix) |
| { |
| group_prefix = ∅ |
| } |
| |
| group_suffix = CONFIG_GET_STRING("Regex", "Append-After-Group"); |
| if (!group_suffix) |
| { |
| group_suffix = ∅ |
| } |
| |
| string = CONFIG_GET_STRING("Regex", "Group-Name-No-Prefix-Regex"); |
| use_gpx = 0; |
| if (string) |
| { |
| status = regcomp(&gpx_re, string, REG_EXTENDED|REG_ICASE); |
| |
| if (status) |
| { |
| warnx("regex_init: compiling regex for group prefix exclusion failed with status %u", status); |
| goto error3; |
| } |
| |
| use_gpx = 1; |
| } |
| |
| return 0; |
| |
| error3: |
| regfree(&group_re); |
| error2: |
| regfree(&user_re); |
| error1: |
| return 0; |
| /* return -EINVAL; */ |
| } |
| |
| |
| struct trans_func regex_trans = { |
| .name = "regex", |
| .init = regex_init, |
| .name_to_uid = regex_name_to_uid, |
| .name_to_gid = regex_name_to_gid, |
| .uid_to_name = regex_uid_to_name, |
| .gid_to_name = regex_gid_to_name, |
| .princ_to_ids = regex_gss_princ_to_ids, |
| .gss_princ_to_grouplist = regex_gss_princ_to_grouplist, |
| }; |
| |
| struct trans_func *libnfsidmap_plugin_init() |
| { |
| return (®ex_trans); |
| } |
| |