/*ARGSUSED*/ static int _dns_initshells(void *rv, void *cb_data, va_list ap) { char shellname[] = "shells-XXXXX"; int hsindex, hpi, r; char **hp; void *context; if (sl) sl_free(sl, 1); sl = sl_init(); r = NS_UNAVAIL; if (hesiod_init(&context) == -1) return (r); for (hsindex = 0; ; hsindex++) { snprintf(shellname, sizeof(shellname)-1, "shells-%d", hsindex); hp = hesiod_resolve(context, shellname, "shells"); if (hp == NULL) { if (errno == ENOENT) { if (hsindex == 0) r = NS_NOTFOUND; else r = NS_SUCCESS; } break; } else { for (hpi = 0; hp[hpi]; hpi++) sl_add(sl, hp[hpi]); free(hp); } } hesiod_end(context); return (r); }
/* * This function takes a hesiod (name, type) and returns a DNS * name which is to be resolved. */ char * hesiod_to_bind(void *context, const char *name, const char *type) { struct hesiod_p *ctx = (struct hesiod_p *) context; char *bindname; char **rhs_list = NULL; const char *RHS, *cp; /* Decide what our RHS is, and set cp to the end of the actual name. */ if ((cp = strchr(name, '@')) != NULL) { if (strchr(cp + 1, '.')) RHS = cp + 1; else if ((rhs_list = hesiod_resolve(context, cp + 1, "rhs-extension")) != NULL) RHS = *rhs_list; else { errno = ENOENT; return (NULL); } } else { RHS = ctx->RHS; cp = name + strlen(name); } /* * Allocate the space we need, including up to three periods and * the terminating NUL. */ if ((bindname = malloc((cp - name) + strlen(type) + strlen(RHS) + (ctx->LHS ? strlen(ctx->LHS) : 0) + 4)) == NULL) { errno = ENOMEM; if (rhs_list) hesiod_free_list(context, rhs_list); return NULL; } /* Now put together the DNS name. */ memcpy(bindname, name, cp - name); bindname[cp - name] = '\0'; strcat(bindname, "."); strcat(bindname, type); if (ctx->LHS) { if (ctx->LHS[0] != '.') strcat(bindname, "."); strcat(bindname, ctx->LHS); } if (RHS[0] != '.') strcat(bindname, "."); strcat(bindname, RHS); if (rhs_list) hesiod_free_list(context, rhs_list); return (bindname); }
static enum nss_status lookup (const char *name, const char *type, struct passwd *pwd, char *buffer, size_t buflen, int *errnop) { struct parser_data *data = (void *) buffer; size_t linebuflen; void *context; char **list; int parse_res; size_t len; int olderr = errno; context = _nss_hesiod_init (); if (context == NULL) return NSS_STATUS_UNAVAIL; list = hesiod_resolve (context, name, type); if (list == NULL) { int err = errno; hesiod_end (context); __set_errno (olderr); return err == ENOENT ? NSS_STATUS_NOTFOUND : NSS_STATUS_UNAVAIL; } linebuflen = buffer + buflen - data->linebuffer; len = strlen (*list) + 1; if (linebuflen < len) { hesiod_free_list (context, list); hesiod_end (context); *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } memcpy (data->linebuffer, *list, len); hesiod_free_list (context, list); hesiod_end (context); parse_res = _nss_files_parse_pwent (buffer, pwd, data, buflen, errnop); if (parse_res < 1) { __set_errno (olderr); return parse_res == -1 ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; } return NSS_STATUS_SUCCESS; }
struct hesiod_postoffice *hesiod_getmailhost(void *context, const char *user) { char *p, **list; struct hesiod_postoffice *po; /* Get the result, sanity-check it, and copy it into linebuf. */ list = hesiod_resolve(context, user, "pobox"); if (!list) return NULL; p = malloc(strlen(*list) + 1); if (!p) { hesiod_free_list(context, list); errno = ENOMEM; return NULL; } strcpy(p, *list); hesiod_free_list(context, list); /* Allocate memory for the result. */ po = (struct hesiod_postoffice *) malloc(sizeof(struct hesiod_postoffice)); if (!po) { free(p); errno = ENOMEM; return NULL; } /* Break up linebuf into fields. */ po->hesiod_po_type = p; while (!isspace((unsigned char)*p)) p++; *p++ = 0; po->hesiod_po_host = p; while (!isspace((unsigned char)*p)) p++; *p++ = 0; po->hesiod_po_name = p; return po; }
static enum nss_status internal_gid_from_group (void *context, const char *groupname, gid_t *group) { char **grp_res; enum nss_status status = NSS_STATUS_NOTFOUND; grp_res = hesiod_resolve (context, groupname, "group"); if (grp_res != NULL && *grp_res != NULL) { char *p = *grp_res; /* Skip to third field. */ while (*p != '\0' && *p != ':') ++p; if (*p != '\0') ++p; while (*p != '\0' && *p != ':') ++p; if (*p != '\0') { char *endp; char *q = ++p; long int val; while (*q != '\0' && *q != ':') ++q; val = strtol (p, &endp, 10); if (sizeof (gid_t) == sizeof (long int) || (gid_t) val == val) { *group = val; if (endp == q && endp != p) status = NSS_STATUS_SUCCESS; } } hesiod_free_list (context, grp_res); } return status; }
static struct passwd *getpwcommon(void *context, const char *arg, int which) { char *p, **list; struct passwd *pw; /* Get the response and copy the first entry into a buffer. */ list = hesiod_resolve(context, arg, which ? "uid" : "passwd"); if (!list) return NULL; p = malloc(strlen(*list) + 1); if (!p) { hesiod_free_list(context, list); errno = ENOMEM; return NULL; } strcpy(p, *list); hesiod_free_list(context, list); /* Allocate memory for the result. */ pw = (struct passwd *) malloc(sizeof(struct passwd)); if (!pw) { free(p); errno = ENOMEM; return NULL; } /* Split up buf into fields. */ pw->pw_name = p; p = next_field(p); pw->pw_passwd = p; p = next_field(p); pw->pw_uid = atoi(p); p = next_field(p); pw->pw_gid = atoi(p); p = next_field(p); pw->pw_gecos = p; p = next_field(p); pw->pw_dir = p; p = next_field(p); pw->pw_shell = p; while (*p && *p != '\n') p++; *p = 0; #ifdef HAVE_PW_QUOTA pw->pw_quota = 0; #endif #ifdef HAVE_PW_COMMENT pw->pw_comment = ""; #endif #ifdef HAVE_PW_CLASS pw->pw_class = ""; #endif #ifdef HAVE_PW_CHANGE pw->pw_change = 0; #endif #ifdef HAVE_PW_EXPIRE pw->pw_expire = 0; #endif return pw; }
/* * __grscan_dns * Search Hesiod for the next desired entry. * If search is zero, return the next entry. * If search is non-zero, look for a specific name (if name != NULL), * or a specific gid (if name == NULL). */ int __grscan_dns(int *retval, struct group *grp, char *buffer, size_t buflen, struct __grstate_dns *state, int search, const char *name, gid_t gid) { const char **curzone; char **hp, *ep; int rv; static const char *zones_gid_group[] = { "gid", "group", NULL }; static const char *zones_group[] = { "group", NULL }; _DIAGASSERT(retval != NULL); _DIAGASSERT(grp != NULL); _DIAGASSERT(buffer != NULL); _DIAGASSERT(state != NULL); /* name is NULL to indicate searching for gid */ *retval = 0; if (state->context == NULL) { /* only start if Hesiod not setup */ rv = __grstart_dns(state); if (rv != NS_SUCCESS) return rv; } next_dns_entry: hp = NULL; rv = NS_NOTFOUND; if (! search) { /* find next entry */ if (state->num == -1) /* exhausted search */ return NS_NOTFOUND; /* find group-NNN */ snprintf(buffer, buflen, "group-%u", state->num); state->num++; curzone = zones_group; } else if (name) { /* find group name */ snprintf(buffer, buflen, "%s", name); curzone = zones_group; } else { /* find gid */ snprintf(buffer, buflen, "%u", (unsigned int)gid); curzone = zones_gid_group; } for (; *curzone; curzone++) { /* search zones */ hp = hesiod_resolve(state->context, buffer, *curzone); if (hp != NULL) break; if (errno != ENOENT) { rv = NS_UNAVAIL; goto dnsgrscan_out; } } if (*curzone == NULL) { if (! search) state->num = -1; goto dnsgrscan_out; } if ((ep = strchr(hp[0], '\n')) != NULL) *ep = '\0'; /* clear trailing \n */ if (_gr_parse(hp[0], grp, buffer, buflen)) { /* validate line */ if (! search) { /* just want this one */ rv = NS_SUCCESS; } else if ((name && strcmp(name, grp->gr_name) == 0) || (!name && gid == grp->gr_gid)) { /* want specific */ rv = NS_SUCCESS; } } else { /* dodgy entry */ if (!search) { /* try again if ! searching */ hesiod_free_list(state->context, hp); goto next_dns_entry; } } dnsgrscan_out: if (rv != NS_SUCCESS && rv != NS_NOTFOUND) *retval = errno; if (hp) hesiod_free_list(state->context, hp); return rv; }
int main(int argc, char **argv) { char **list, **p, *bindname, *name, *type; int lflag = 0, errflg = 0, bflag = 0, c; void *context; while ((c = getopt(argc, argv, "lb")) != -1) { switch (c) { case 'l': lflag = 1; break; case 'b': bflag = 1; break; default: errflg++; break; } } if (argc - optind != 2 || errflg) { fprintf(stderr, "usage: hesinfo [-bl] name type\n"); fprintf(stderr, "\t-l selects long format\n"); fprintf(stderr, "\t-b also does hes_to_bind conversion\n"); exit(2); } name = argv[optind]; type = argv[optind + 1]; if (hesiod_init(&context) < 0) { if (errno == ENOEXEC) warnx( "hesiod_init: Invalid Hesiod configuration file."); else warn("hesiod_init"); } /* Display bind name if requested. */ if (bflag) { if (lflag) printf("hes_to_bind(%s, %s) expands to\n", name, type); bindname = hesiod_to_bind(context, name, type); if (!bindname) { if (lflag) printf("nothing\n"); if (errno == ENOENT) warnx("hesiod_to_bind: Unknown rhs-extension."); else warn("hesiod_to_bind"); exit(1); } printf("%s\n", bindname); free(bindname); if (lflag) printf("which "); } if (lflag) printf("resolves to\n"); /* Do the hesiod resolve and check for errors. */ list = hesiod_resolve(context, name, type); if (!list) { if (lflag) printf("nothing\n"); if (errno == ENOENT) warnx("hesiod_resolve: Hesiod name not found."); else warn("hesiod_resolve"); exit(1); } /* Display the results. */ for (p = list; *p; p++) printf("%s\n", *p); hesiod_free_list(context, list); hesiod_end(context); exit(0); }
/* Retrieve the user's hesiod groups and stuff them into *groups, with a * count in *ngroups. Also put the user's primary gid into *primary_gid. * Return 0 on success and -1 on failure. */ static int retrieve_hesgroups(const char *username, struct hesgroup **groups, int *ngroups, gid_t *primary_gid) { char **grplistvec, **primarygidvec, *primary_name, buf[64], *p, *q; int n, len; struct hesgroup *hesgroups; struct passwd *pwd; void *hescontext; /* Look up the user's primary group in hesiod to retrieve the primary * group name. Start by finding the gid. */ pwd = al__getpwnam(username); if (!pwd) return -1; *primary_gid = pwd->pw_gid; al__free_passwd(pwd); /* Initialize the hesiod context. */ if (hesiod_init(&hescontext) != 0) return -1; /* Now do the hesiod resolve. If it fails with ENOENT, assume the user * has a local account and return no groups. */ sprintf(buf, "%lu", (unsigned long) *primary_gid); primarygidvec = hesiod_resolve(hescontext, buf, "gid"); if (!primarygidvec && errno == ENOENT) { *groups = NULL; *ngroups = 0; hesiod_end(hescontext); return 0; } if (!primarygidvec || !*primarygidvec || **primarygidvec == ':') { if (primarygidvec) hesiod_free_list(hescontext, primarygidvec); hesiod_end(hescontext); return -1; } /* Copy the name part into primary_name. */ p = strchr(*primarygidvec, ':'); len = (p) ? p - *primarygidvec : strlen(*primarygidvec); primary_name = malloc(len + 1); if (!primary_name) { hesiod_free_list(hescontext, primarygidvec); hesiod_end(hescontext); return -1; } memcpy(primary_name, *primarygidvec, len); primary_name[len] = 0; hesiod_free_list(hescontext, primarygidvec); /* Look up the Hesiod group list. It's okay if there isn't one. */ grplistvec = hesiod_resolve(hescontext, username, "grplist"); if ((!grplistvec && errno != ENOENT) || (grplistvec && !*grplistvec)) { if (grplistvec) hesiod_free_list(hescontext, grplistvec); hesiod_end(hescontext); free(primary_name); return -1; } /* Get a close upper bound on the number of group entries we'll need. */ if (grplistvec) { n = 0; for (p = *grplistvec; *p; p++) { if (*p == ':') n++; } n = (n + 1) / 2 + 1; } else n = 1; /* Allocate memory. */ hesgroups = malloc(n * sizeof(struct hesgroup)); if (!hesgroups) { if (grplistvec) hesiod_free_list(hescontext, grplistvec); hesiod_end(hescontext); free(primary_name); return -1; } /* Start off the list with the primary gid. */ hesgroups[0].name = primary_name; hesgroups[0].gid = *primary_gid; hesgroups[0].present = 0; n = 1; /* Now get the entries from grplistvec, if we got one. */ p = (grplistvec) ? *grplistvec : NULL; while (p) { /* Find the end of the group name. Stop if we hit the end, if we * have a zero-length group name, or if we have a non-numeric gid. */ q = strchr(p, ':'); if (!q || q == p || !isdigit((unsigned char)*(q + 1))) break; if (atoi(q + 1) >= MIN_HES_GROUP) { hesgroups[n].name = malloc(q - p + 1); if (!hesgroups[n].name) { free_hesgroups(hesgroups, n); hesiod_free_list(hescontext, grplistvec); hesiod_end(hescontext); return -1; } memcpy(hesgroups[n].name, p, q - p); hesgroups[n].name[q - p] = 0; hesgroups[n].gid = atoi(q + 1); hesgroups[n].present = 0; n++; } p = strchr(q + 1, ':'); if (p) p++; } /* Clean up allocated memory and return. */ if (grplistvec) hesiod_free_list(hescontext, grplistvec); hesiod_end(hescontext); *ngroups = n; *groups = hesgroups; return 0; }
enum nss_status _nss_hesiod_initgroups_dyn (const char *user, gid_t group, long int *start, long int *size, gid_t **groupsp, long int limit, int *errnop) { enum nss_status status = NSS_STATUS_SUCCESS; char **list = NULL; char *p; void *context; gid_t *groups = *groupsp; int save_errno; context = _nss_hesiod_init (); if (context == NULL) return NSS_STATUS_UNAVAIL; list = hesiod_resolve (context, user, "grplist"); if (list == NULL) { hesiod_end (context); return errno == ENOENT ? NSS_STATUS_NOTFOUND : NSS_STATUS_UNAVAIL; } save_errno = errno; p = *list; while (*p != '\0') { char *endp; char *q; long int val; status = NSS_STATUS_NOTFOUND; q = p; while (*q != '\0' && *q != ':' && *q != ',') ++q; if (*q != '\0') *q++ = '\0'; __set_errno (0); val = strtol (p, &endp, 10); /* Test whether the number is representable in a variable of type `gid_t'. If not ignore the number. */ if ((sizeof (gid_t) == sizeof (long int) || (gid_t) val == val) && errno == 0) { if (*endp == '\0' && endp != p) { group = val; status = NSS_STATUS_SUCCESS; } else status = internal_gid_from_group (context, p, &group); if (status == NSS_STATUS_SUCCESS && !internal_gid_in_list (groups, group, *start)) { if (__builtin_expect (*start == *size, 0)) { /* Need a bigger buffer. */ gid_t *newgroups; long int newsize; if (limit > 0 && *size == limit) /* We reached the maximum. */ goto done; if (limit <= 0) newsize = 2 * *size; else newsize = MIN (limit, 2 * *size); newgroups = realloc (groups, newsize * sizeof (*groups)); if (newgroups == NULL) goto done; *groupsp = groups = newgroups; *size = newsize; } groups[(*start)++] = group; } } p = q; } __set_errno (save_errno); done: hesiod_free_list (context, list); hesiod_end (context); return NSS_STATUS_SUCCESS; }
/* This function takes a hesiod (name, type) and returns a DNS * name which is to be resolved. */ char *hesiod_to_bind(void *context, const char *name, const char *type) { struct hesiod_p *ctx = (struct hesiod_p *) context; char bindname[MAXDNAME], *p, *ret, *idn_ret, **rhs_list = NULL; const char *rhs; int len, rc; if (strlen(name) > sizeof(bindname) - 1) { errno = EMSGSIZE; return NULL; } strcpy(bindname, name); /* Find the right right hand side to use, possibly truncating bindname. */ p = strchr(bindname, '@'); if (p) { *p++ = 0; if (strchr(p, '.')) rhs = name + (p - bindname); else { rhs_list = hesiod_resolve(context, p, "rhs-extension"); if (rhs_list) rhs = *rhs_list; else { errno = ENOENT; return NULL; } } } else rhs = ctx->rhs; /* See if we have enough room. */ len = strlen(bindname) + 1 + strlen(type); if (ctx->lhs) len += strlen(ctx->lhs) + ((ctx->lhs[0] != '.') ? 1 : 0); len += strlen(rhs) + ((rhs[0] != '.') ? 1 : 0); if (len > sizeof(bindname) - 1) { if (rhs_list) hesiod_free_list(context, rhs_list); errno = EMSGSIZE; return NULL; } /* Put together the rest of the domain. */ strcat(bindname, "."); strcat(bindname, type); if (ctx->lhs) { if (ctx->lhs[0] != '.') strcat(bindname, "."); strcat(bindname, ctx->lhs); } if (rhs[0] != '.') strcat(bindname, "."); strcat(bindname, rhs); /* rhs_list is no longer needed, since we're done with rhs. */ if (rhs_list) hesiod_free_list(context, rhs_list); /* Make a copy of the result and return it to the caller. */ #ifdef HAVE_LIBIDN rc = idna_to_ascii_lz(bindname, &idn_ret, 0); if (rc != IDNA_SUCCESS) { errno = EINVAL; return NULL; } ret = strdup(idn_ret); idn_free(idn_ret); #else ret = strdup(bindname); #endif if (!ret) { errno = ENOMEM; return NULL; } return ret; }