/* Authors: Benjamin Franzke Copyright (C) 2013 Benjamin Franzke This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* TODO: Support of [all] samba's Unix SIDs: * Users: S-1-22-1-%UID * Groups: S-1-22-2-%GID */ #include #include #include #include #include #include #include #include "lib/idmap/sss_idmap.h" #include "sss_client/idmap/sss_nss_idmap.h" #ifdef DEBUG #include #define debug(str, ...) \ syslog(0, "%s: " str "\n", \ __FUNCTION__, ##__VA_ARGS__) #else #define debug(...) do { } while(0) #endif struct sssd_ctx { struct sss_idmap_ctx *idmap; const char **errmsg; }; #define ctx_set_error(ctx, error) \ do { \ *ctx->errmsg = error; \ debug("%s", error ? error : ""); \ } while (0); int cifs_idmap_init_plugin(void **handle, const char **errmsg) { struct sssd_ctx *ctx; enum idmap_error_code err; if (handle == NULL || errmsg == NULL) return EINVAL; ctx = malloc(sizeof *ctx); if (!ctx) { *errmsg = "Failed to allocate context"; return -1; } ctx->errmsg = errmsg; ctx_set_error(ctx, NULL); err = sss_idmap_init(NULL, NULL, NULL, &ctx->idmap); if (err != IDMAP_SUCCESS) { ctx_set_error(ctx, idmap_error_string(err)); free(ctx); return -1; } *handle = ctx; return 0; } void cifs_idmap_exit_plugin(void *handle) { struct sssd_ctx *ctx = handle; debug("exit"); if (ctx == NULL) return; sss_idmap_free(ctx->idmap); free(ctx); } /* Test with `getcifsacl file` on client. */ int cifs_idmap_sid_to_str(void *handle, const struct cifs_sid *csid, char **name) { struct sssd_ctx *ctx = handle; enum idmap_error_code iderr; char *sid; enum sss_id_type id_type; int err; iderr = sss_idmap_bin_sid_to_sid(ctx->idmap, (const uint8_t *) csid, sizeof(*csid), &sid); if (iderr != IDMAP_SUCCESS) { ctx_set_error(ctx, idmap_error_string(iderr)); *name = NULL; return -1; } debug("sid: %s", sid); err = sss_nss_getnamebysid(sid, name, &id_type); if (err != 0) { ctx_set_error(ctx, strerror(err)); *name = NULL; return -err; } /* FIXME: Map Samba Unix SIDs? (sid->id and use getpwuid)? */ debug("name: %s", *name); return 0; } static int sid_to_cifs_sid(struct sssd_ctx *ctx, const char *sid, struct cifs_sid *csid) { uint8_t *bsid = NULL; enum idmap_error_code err; size_t length; err = sss_idmap_sid_to_bin_sid(ctx->idmap, sid, &bsid, &length); if (err != IDMAP_SUCCESS) { ctx_set_error(ctx, idmap_error_string(err)); return -1; } if (length > sizeof(struct cifs_sid)) { ctx_set_error(ctx, "too large sid length"); free(bsid); return -1; } memcpy(csid, bsid, length); sss_idmap_free_bin_sid(ctx->idmap, bsid); return 0; } /* Test with setcifsacl -a */ int cifs_idmap_str_to_sid(void *handle, const char *name, struct cifs_sid *csid) { struct sssd_ctx *ctx = handle; int err; enum sss_id_type id_type; char *sid = NULL; int success = 0; debug("%s", name); err = sss_nss_getsidbyname(name, &sid, &id_type); if (err != 0) { /* Might be a raw string representation of SID, * try converting that before returning an error. */ if (sid_to_cifs_sid(ctx, name, csid) == 0) return 0; ctx_set_error(ctx, strerror(err)); return -err; } if (sid_to_cifs_sid(ctx, sid, csid) != 0) success = -1; free(sid); return success; } static int samba_unix_sid_to_id(const char *sid, struct cifs_uxid *cuxid) { id_t id; uint8_t type; if (sscanf(sid, "S-1-22-%hhu-%u", &type, &id) != 2) return -1; switch (type) { case 1: cuxid->type = CIFS_UXID_TYPE_UID; cuxid->id.uid = id; break; case 2: cuxid->type = CIFS_UXID_TYPE_GID; cuxid->id.gid = id; break; default: cuxid->type = CIFS_UXID_TYPE_UNKNOWN; return -1; } return 0; } static int sss_sid_to_id(struct sssd_ctx *ctx, const char *sid, struct cifs_uxid *cuxid) { int err; enum sss_id_type id_type; err = sss_nss_getidbysid(sid, (uint32_t *)&cuxid->id.uid, &id_type); if (err != 0) { ctx_set_error(ctx, strerror(err)); return -1; } switch (id_type) { case SSS_ID_TYPE_UID: cuxid->type = CIFS_UXID_TYPE_UID; break; case SSS_ID_TYPE_GID: cuxid->type = CIFS_UXID_TYPE_GID; break; case SSS_ID_TYPE_BOTH: cuxid->type = CIFS_UXID_TYPE_BOTH; break; case SSS_ID_TYPE_NOT_SPECIFIED: default: return -1; } return 0; } /** * cifs_idmap_sids_to_ids - convert struct cifs_sids to struct cifs_uxids * usecase: mount.cifs -o sec=krb5,multiuser,cifsacl,nounix * test: ls -n on mounted share */ int cifs_idmap_sids_to_ids(void *handle, const struct cifs_sid *csid, const size_t num, struct cifs_uxid *cuxid) { struct sssd_ctx *ctx = handle; enum idmap_error_code err; int success = -1; size_t i; char *sid; debug("num: %zd", num); if (num > UINT_MAX) { ctx_set_error(ctx, "num is too large."); return EINVAL; } for (i = 0; i < num; ++i) { err = sss_idmap_bin_sid_to_sid(ctx->idmap, (const uint8_t *) &csid[i], sizeof(csid[i]), &sid); if (err != IDMAP_SUCCESS) { ctx_set_error(ctx, idmap_error_string(err)); continue; } cuxid[i].type = CIFS_UXID_TYPE_UNKNOWN; if (sss_sid_to_id(ctx, sid, &cuxid[i]) == 0 || samba_unix_sid_to_id(sid, &cuxid[i]) == 0) { debug("setting uid of %s to %d", sid, cuxid[i].id.uid); success = 0; } free(sid); } return success; } int cifs_idmap_ids_to_sids(void *handle, const struct cifs_uxid *cuxid, const size_t num, struct cifs_sid *csid) { struct sssd_ctx *ctx = handle; int err, success = -1; char *sid; enum sss_id_type id_type; size_t i; debug("num ids: %zd", num); if (num > UINT_MAX) { ctx_set_error(ctx, "num is too large."); return EINVAL; } for (i = 0; i < num; ++i) { err = sss_nss_getsidbyid((uint32_t)cuxid[i].id.uid, &sid, &id_type); if (err != 0) { ctx_set_error(ctx, strerror(err)); csid[i].revision = 0; /* FIXME: would it be safe to map *any* uid/gids unknown by sssd to * SAMBA's UNIX SIDs? */ continue; } if (sid_to_cifs_sid(ctx, sid, csid) == 0) success = 0; else csid[i].revision = 0; free(sid); } return success; }