| 1 |
/*- |
| 2 |
* Copyright (c) 2003 Networks Associates Technology, Inc. |
| 3 |
* All rights reserved. |
| 4 |
* |
| 5 |
* This software was developed for the FreeBSD Project by |
| 6 |
* Jacques A. Vidrine, Safeport Network Services, and Network |
| 7 |
* Associates Laboratories, the Security Research Division of Network |
| 8 |
* Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 |
| 9 |
* ("CBOSS"), as part of the DARPA CHATS research program. |
| 10 |
* |
| 11 |
* Redistribution and use in source and binary forms, with or without |
| 12 |
* modification, are permitted provided that the following conditions |
| 13 |
* are met: |
| 14 |
* 1. Redistributions of source code must retain the above copyright |
| 15 |
* notice, this list of conditions and the following disclaimer. |
| 16 |
* 2. Redistributions in binary form must reproduce the above copyright |
| 17 |
* notice, this list of conditions and the following disclaimer in the |
| 18 |
* documentation and/or other materials provided with the distribution. |
| 19 |
* |
| 20 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| 21 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 22 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 23 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| 24 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 25 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 26 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 27 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 28 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 29 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 30 |
* SUCH DAMAGE. |
| 31 |
* |
| 32 |
*/ |
| 33 |
#include <sys/cdefs.h> |
| 34 |
__FBSDID("$FreeBSD$"); |
| 35 |
|
| 36 |
#include "namespace.h" |
| 37 |
#include <sys/param.h> |
| 38 |
#ifdef YP |
| 39 |
#include <rpc/rpc.h> |
| 40 |
#include <rpcsvc/yp_prot.h> |
| 41 |
#include <rpcsvc/ypclnt.h> |
| 42 |
#endif |
| 43 |
#include <arpa/inet.h> |
| 44 |
#include <errno.h> |
| 45 |
#include <fcntl.h> |
| 46 |
#ifdef HESIOD |
| 47 |
#include <hesiod.h> |
| 48 |
#endif |
| 49 |
#include <netdb.h> |
| 50 |
#include <nsswitch.h> |
| 51 |
#include <pthread.h> |
| 52 |
#include <pthread_np.h> |
| 53 |
#include <pwd.h> |
| 54 |
#include <stdlib.h> |
| 55 |
#include <stdio.h> |
| 56 |
#include <string.h> |
| 57 |
#include <syslog.h> |
| 58 |
#include <unistd.h> |
| 59 |
#include "un-namespace.h" |
| 60 |
#include <db.h> |
| 61 |
#include "libc_private.h" |
| 62 |
#include "pw_scan.h" |
| 63 |
#include "nss_tls.h" |
| 64 |
#ifdef NS_CACHING |
| 65 |
#include "nscache.h" |
| 66 |
#endif |
| 67 |
|
| 68 |
#ifndef CTASSERT |
| 69 |
#define CTASSERT(x) _CTASSERT(x, __LINE__) |
| 70 |
#define _CTASSERT(x, y) __CTASSERT(x, y) |
| 71 |
#define __CTASSERT(x, y) typedef char __assert_ ## y [(x) ? 1 : -1] |
| 72 |
#endif |
| 73 |
|
| 74 |
/* Counter as stored in /etc/pwd.db */ |
| 75 |
typedef int pwkeynum; |
| 76 |
|
| 77 |
CTASSERT(MAXLOGNAME > sizeof(uid_t)); |
| 78 |
CTASSERT(MAXLOGNAME > sizeof(pwkeynum)); |
| 79 |
|
| 80 |
enum constants { |
| 81 |
PWD_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ |
| 82 |
PWD_STORAGE_MAX = 1 << 20, /* 1 MByte */ |
| 83 |
SETPWENT = 1, |
| 84 |
ENDPWENT = 2, |
| 85 |
HESIOD_NAME_MAX = 256 |
| 86 |
}; |
| 87 |
|
| 88 |
static const ns_src defaultsrc[] = { |
| 89 |
{ NSSRC_COMPAT, NS_SUCCESS }, |
| 90 |
{ NULL, 0 } |
| 91 |
}; |
| 92 |
|
| 93 |
int __pw_match_entry(const char *, size_t, enum nss_lookup_type, |
| 94 |
const char *, uid_t); |
| 95 |
int __pw_parse_entry(char *, size_t, struct passwd *, int, int *errnop); |
| 96 |
|
| 97 |
static void pwd_init(struct passwd *); |
| 98 |
|
| 99 |
union key { |
| 100 |
const char *name; |
| 101 |
uid_t uid; |
| 102 |
}; |
| 103 |
|
| 104 |
static struct passwd *getpw(int (*fn)(union key, struct passwd *, char *, |
| 105 |
size_t, struct passwd **), union key); |
| 106 |
static int wrap_getpwnam_r(union key, struct passwd *, char *, |
| 107 |
size_t, struct passwd **); |
| 108 |
static int wrap_getpwuid_r(union key, struct passwd *, char *, size_t, |
| 109 |
struct passwd **); |
| 110 |
static int wrap_getpwent_r(union key, struct passwd *, char *, size_t, |
| 111 |
struct passwd **); |
| 112 |
|
| 113 |
static int pwdb_match_entry_v3(char *, size_t, enum nss_lookup_type, |
| 114 |
const char *, uid_t); |
| 115 |
static int pwdb_parse_entry_v3(char *, size_t, struct passwd *, int *); |
| 116 |
static int pwdb_match_entry_v4(char *, size_t, enum nss_lookup_type, |
| 117 |
const char *, uid_t); |
| 118 |
static int pwdb_parse_entry_v4(char *, size_t, struct passwd *, int *); |
| 119 |
|
| 120 |
|
| 121 |
struct { |
| 122 |
int (*match)(char *, size_t, enum nss_lookup_type, const char *, |
| 123 |
uid_t); |
| 124 |
int (*parse)(char *, size_t, struct passwd *, int *); |
| 125 |
} pwdb_versions[] = { |
| 126 |
{ NULL, NULL }, /* version 0 */ |
| 127 |
{ NULL, NULL }, /* version 1 */ |
| 128 |
{ NULL, NULL }, /* version 2 */ |
| 129 |
{ pwdb_match_entry_v3, pwdb_parse_entry_v3 }, /* version 3 */ |
| 130 |
{ pwdb_match_entry_v4, pwdb_parse_entry_v4 }, /* version 4 */ |
| 131 |
}; |
| 132 |
|
| 133 |
|
| 134 |
struct files_state { |
| 135 |
DB *db; |
| 136 |
pwkeynum keynum; |
| 137 |
int stayopen; |
| 138 |
int version; |
| 139 |
}; |
| 140 |
static void files_endstate(void *); |
| 141 |
NSS_TLS_HANDLING(files); |
| 142 |
static DB *pwdbopen(int *); |
| 143 |
static void files_endstate(void *); |
| 144 |
static int files_setpwent(void *, void *, va_list); |
| 145 |
static int files_passwd(void *, void *, va_list); |
| 146 |
|
| 147 |
|
| 148 |
#ifdef HESIOD |
| 149 |
struct dns_state { |
| 150 |
long counter; |
| 151 |
}; |
| 152 |
static void dns_endstate(void *); |
| 153 |
NSS_TLS_HANDLING(dns); |
| 154 |
static int dns_setpwent(void *, void *, va_list); |
| 155 |
static int dns_passwd(void *, void *, va_list); |
| 156 |
#endif |
| 157 |
|
| 158 |
|
| 159 |
#ifdef YP |
| 160 |
struct nis_state { |
| 161 |
char domain[MAXHOSTNAMELEN]; |
| 162 |
int done; |
| 163 |
char *key; |
| 164 |
int keylen; |
| 165 |
}; |
| 166 |
static void nis_endstate(void *); |
| 167 |
NSS_TLS_HANDLING(nis); |
| 168 |
static int nis_setpwent(void *, void *, va_list); |
| 169 |
static int nis_passwd(void *, void *, va_list); |
| 170 |
static int nis_map(char *, enum nss_lookup_type, char *, size_t, int *); |
| 171 |
static int nis_adjunct(char *, const char *, char *, size_t); |
| 172 |
#endif |
| 173 |
|
| 174 |
|
| 175 |
struct compat_state { |
| 176 |
DB *db; |
| 177 |
pwkeynum keynum; |
| 178 |
int stayopen; |
| 179 |
int version; |
| 180 |
DB *exclude; |
| 181 |
struct passwd template; |
| 182 |
char *name; |
| 183 |
enum _compat { |
| 184 |
COMPAT_MODE_OFF = 0, |
| 185 |
COMPAT_MODE_ALL, |
| 186 |
COMPAT_MODE_NAME, |
| 187 |
COMPAT_MODE_NETGROUP |
| 188 |
} compat; |
| 189 |
}; |
| 190 |
static void compat_endstate(void *); |
| 191 |
NSS_TLS_HANDLING(compat); |
| 192 |
static int compat_setpwent(void *, void *, va_list); |
| 193 |
static int compat_passwd(void *, void *, va_list); |
| 194 |
static void compat_clear_template(struct passwd *); |
| 195 |
static int compat_set_template(struct passwd *, struct passwd *); |
| 196 |
static int compat_use_template(struct passwd *, struct passwd *, char *, |
| 197 |
size_t); |
| 198 |
static int compat_redispatch(struct compat_state *, enum nss_lookup_type, |
| 199 |
enum nss_lookup_type, const char *, const char *, uid_t, |
| 200 |
struct passwd *, char *, size_t, int *); |
| 201 |
|
| 202 |
#ifdef NS_CACHING |
| 203 |
static int pwd_id_func(char *, size_t *, va_list ap, void *); |
| 204 |
static int pwd_marshal_func(char *, size_t *, void *, va_list, void *); |
| 205 |
static int pwd_unmarshal_func(char *, size_t, void *, va_list, void *); |
| 206 |
|
| 207 |
static int |
| 208 |
pwd_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) |
| 209 |
{ |
| 210 |
char *name; |
| 211 |
uid_t uid; |
| 212 |
size_t size, desired_size; |
| 213 |
int res = NS_UNAVAIL; |
| 214 |
enum nss_lookup_type lookup_type; |
| 215 |
|
| 216 |
lookup_type = (enum nss_lookup_type)cache_mdata; |
| 217 |
switch (lookup_type) { |
| 218 |
case nss_lt_name: |
| 219 |
name = va_arg(ap, char *); |
| 220 |
size = strlen(name); |
| 221 |
desired_size = sizeof(enum nss_lookup_type) + size + 1; |
| 222 |
if (desired_size > *buffer_size) { |
| 223 |
res = NS_RETURN; |
| 224 |
goto fin; |
| 225 |
} |
| 226 |
|
| 227 |
memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); |
| 228 |
memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); |
| 229 |
|
| 230 |
res = NS_SUCCESS; |
| 231 |
break; |
| 232 |
case nss_lt_id: |
| 233 |
uid = va_arg(ap, uid_t); |
| 234 |
desired_size = sizeof(enum nss_lookup_type) + sizeof(uid_t); |
| 235 |
if (desired_size > *buffer_size) { |
| 236 |
res = NS_RETURN; |
| 237 |
goto fin; |
| 238 |
} |
| 239 |
|
| 240 |
memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); |
| 241 |
memcpy(buffer + sizeof(enum nss_lookup_type), &uid, |
| 242 |
sizeof(uid_t)); |
| 243 |
|
| 244 |
res = NS_SUCCESS; |
| 245 |
break; |
| 246 |
default: |
| 247 |
/* should be unreachable */ |
| 248 |
return (NS_UNAVAIL); |
| 249 |
} |
| 250 |
|
| 251 |
fin: |
| 252 |
*buffer_size = desired_size; |
| 253 |
return (res); |
| 254 |
} |
| 255 |
|
| 256 |
static int |
| 257 |
pwd_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, |
| 258 |
void *cache_mdata) |
| 259 |
{ |
| 260 |
char *name; |
| 261 |
uid_t uid; |
| 262 |
struct passwd *pwd; |
| 263 |
char *orig_buf; |
| 264 |
size_t orig_buf_size; |
| 265 |
|
| 266 |
struct passwd new_pwd; |
| 267 |
size_t desired_size, size; |
| 268 |
char *p; |
| 269 |
|
| 270 |
switch ((enum nss_lookup_type)cache_mdata) { |
| 271 |
case nss_lt_name: |
| 272 |
name = va_arg(ap, char *); |
| 273 |
break; |
| 274 |
case nss_lt_id: |
| 275 |
uid = va_arg(ap, uid_t); |
| 276 |
break; |
| 277 |
case nss_lt_all: |
| 278 |
break; |
| 279 |
default: |
| 280 |
/* should be unreachable */ |
| 281 |
return (NS_UNAVAIL); |
| 282 |
} |
| 283 |
|
| 284 |
pwd = va_arg(ap, struct passwd *); |
| 285 |
orig_buf = va_arg(ap, char *); |
| 286 |
orig_buf_size = va_arg(ap, size_t); |
| 287 |
|
| 288 |
desired_size = sizeof(struct passwd) + sizeof(char *) + |
| 289 |
strlen(pwd->pw_name) + 1; |
| 290 |
if (pwd->pw_passwd != NULL) |
| 291 |
desired_size += strlen(pwd->pw_passwd) + 1; |
| 292 |
if (pwd->pw_class != NULL) |
| 293 |
desired_size += strlen(pwd->pw_class) + 1; |
| 294 |
if (pwd->pw_gecos != NULL) |
| 295 |
desired_size += strlen(pwd->pw_gecos) + 1; |
| 296 |
if (pwd->pw_dir != NULL) |
| 297 |
desired_size += strlen(pwd->pw_dir) + 1; |
| 298 |
if (pwd->pw_shell != NULL) |
| 299 |
desired_size += strlen(pwd->pw_shell) + 1; |
| 300 |
|
| 301 |
if (*buffer_size < desired_size) { |
| 302 |
/* this assignment is here for future use */ |
| 303 |
*buffer_size = desired_size; |
| 304 |
return (NS_RETURN); |
| 305 |
} |
| 306 |
|
| 307 |
memcpy(&new_pwd, pwd, sizeof(struct passwd)); |
| 308 |
memset(buffer, 0, desired_size); |
| 309 |
|
| 310 |
*buffer_size = desired_size; |
| 311 |
p = buffer + sizeof(struct passwd) + sizeof(char *); |
| 312 |
memcpy(buffer + sizeof(struct passwd), &p, sizeof(char *)); |
| 313 |
|
| 314 |
if (new_pwd.pw_name != NULL) { |
| 315 |
size = strlen(new_pwd.pw_name); |
| 316 |
memcpy(p, new_pwd.pw_name, size); |
| 317 |
new_pwd.pw_name = p; |
| 318 |
p += size + 1; |
| 319 |
} |
| 320 |
|
| 321 |
if (new_pwd.pw_passwd != NULL) { |
| 322 |
size = strlen(new_pwd.pw_passwd); |
| 323 |
memcpy(p, new_pwd.pw_passwd, size); |
| 324 |
new_pwd.pw_passwd = p; |
| 325 |
p += size + 1; |
| 326 |
} |
| 327 |
|
| 328 |
if (new_pwd.pw_class != NULL) { |
| 329 |
size = strlen(new_pwd.pw_class); |
| 330 |
memcpy(p, new_pwd.pw_class, size); |
| 331 |
new_pwd.pw_class = p; |
| 332 |
p += size + 1; |
| 333 |
} |
| 334 |
|
| 335 |
if (new_pwd.pw_gecos != NULL) { |
| 336 |
size = strlen(new_pwd.pw_gecos); |
| 337 |
memcpy(p, new_pwd.pw_gecos, size); |
| 338 |
new_pwd.pw_gecos = p; |
| 339 |
p += size + 1; |
| 340 |
} |
| 341 |
|
| 342 |
if (new_pwd.pw_dir != NULL) { |
| 343 |
size = strlen(new_pwd.pw_dir); |
| 344 |
memcpy(p, new_pwd.pw_dir, size); |
| 345 |
new_pwd.pw_dir = p; |
| 346 |
p += size + 1; |
| 347 |
} |
| 348 |
|
| 349 |
if (new_pwd.pw_shell != NULL) { |
| 350 |
size = strlen(new_pwd.pw_shell); |
| 351 |
memcpy(p, new_pwd.pw_shell, size); |
| 352 |
new_pwd.pw_shell = p; |
| 353 |
p += size + 1; |
| 354 |
} |
| 355 |
|
| 356 |
memcpy(buffer, &new_pwd, sizeof(struct passwd)); |
| 357 |
return (NS_SUCCESS); |
| 358 |
} |
| 359 |
|
| 360 |
static int |
| 361 |
pwd_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, |
| 362 |
void *cache_mdata) |
| 363 |
{ |
| 364 |
char *name; |
| 365 |
uid_t uid; |
| 366 |
struct passwd *pwd; |
| 367 |
char *orig_buf; |
| 368 |
size_t orig_buf_size; |
| 369 |
int *ret_errno; |
| 370 |
|
| 371 |
char *p; |
| 372 |
|
| 373 |
switch ((enum nss_lookup_type)cache_mdata) { |
| 374 |
case nss_lt_name: |
| 375 |
name = va_arg(ap, char *); |
| 376 |
break; |
| 377 |
case nss_lt_id: |
| 378 |
uid = va_arg(ap, uid_t); |
| 379 |
break; |
| 380 |
case nss_lt_all: |
| 381 |
break; |
| 382 |
default: |
| 383 |
/* should be unreachable */ |
| 384 |
return (NS_UNAVAIL); |
| 385 |
} |
| 386 |
|
| 387 |
pwd = va_arg(ap, struct passwd *); |
| 388 |
orig_buf = va_arg(ap, char *); |
| 389 |
orig_buf_size = va_arg(ap, size_t); |
| 390 |
ret_errno = va_arg(ap, int *); |
| 391 |
|
| 392 |
if (orig_buf_size < |
| 393 |
buffer_size - sizeof(struct passwd) - sizeof(char *)) { |
| 394 |
*ret_errno = ERANGE; |
| 395 |
return (NS_RETURN); |
| 396 |
} |
| 397 |
|
| 398 |
memcpy(pwd, buffer, sizeof(struct passwd)); |
| 399 |
memcpy(&p, buffer + sizeof(struct passwd), sizeof(char *)); |
| 400 |
memcpy(orig_buf, buffer + sizeof(struct passwd) + sizeof(char *), |
| 401 |
buffer_size - sizeof(struct passwd) - sizeof(char *)); |
| 402 |
|
| 403 |
NS_APPLY_OFFSET(pwd->pw_name, orig_buf, p, char *); |
| 404 |
NS_APPLY_OFFSET(pwd->pw_passwd, orig_buf, p, char *); |
| 405 |
NS_APPLY_OFFSET(pwd->pw_class, orig_buf, p, char *); |
| 406 |
NS_APPLY_OFFSET(pwd->pw_gecos, orig_buf, p, char *); |
| 407 |
NS_APPLY_OFFSET(pwd->pw_dir, orig_buf, p, char *); |
| 408 |
NS_APPLY_OFFSET(pwd->pw_shell, orig_buf, p, char *); |
| 409 |
|
| 410 |
if (retval != NULL) |
| 411 |
*((struct passwd **)retval) = pwd; |
| 412 |
|
| 413 |
return (NS_SUCCESS); |
| 414 |
} |
| 415 |
|
| 416 |
NSS_MP_CACHE_HANDLING(passwd); |
| 417 |
#endif /* NS_CACHING */ |
| 418 |
|
| 419 |
void |
| 420 |
setpwent(void) |
| 421 |
{ |
| 422 |
#ifdef NS_CACHING |
| 423 |
static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( |
| 424 |
passwd, (void *)nss_lt_all, |
| 425 |
NULL, NULL); |
| 426 |
#endif |
| 427 |
|
| 428 |
static const ns_dtab dtab[] = { |
| 429 |
{ NSSRC_FILES, files_setpwent, (void *)SETPWENT }, |
| 430 |
#ifdef HESIOD |
| 431 |
{ NSSRC_DNS, dns_setpwent, (void *)SETPWENT }, |
| 432 |
#endif |
| 433 |
#ifdef YP |
| 434 |
{ NSSRC_NIS, nis_setpwent, (void *)SETPWENT }, |
| 435 |
#endif |
| 436 |
{ NSSRC_COMPAT, compat_setpwent, (void *)SETPWENT }, |
| 437 |
#ifdef NS_CACHING |
| 438 |
NS_CACHE_CB(&cache_info) |
| 439 |
#endif |
| 440 |
{ NULL, NULL, NULL } |
| 441 |
}; |
| 442 |
(void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc, 0); |
| 443 |
} |
| 444 |
|
| 445 |
|
| 446 |
int |
| 447 |
setpassent(int stayopen) |
| 448 |
{ |
| 449 |
#ifdef NS_CACHING |
| 450 |
static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( |
| 451 |
passwd, (void *)nss_lt_all, |
| 452 |
NULL, NULL); |
| 453 |
#endif |
| 454 |
|
| 455 |
static const ns_dtab dtab[] = { |
| 456 |
{ NSSRC_FILES, files_setpwent, (void *)SETPWENT }, |
| 457 |
#ifdef HESIOD |
| 458 |
{ NSSRC_DNS, dns_setpwent, (void *)SETPWENT }, |
| 459 |
#endif |
| 460 |
#ifdef YP |
| 461 |
{ NSSRC_NIS, nis_setpwent, (void *)SETPWENT }, |
| 462 |
#endif |
| 463 |
{ NSSRC_COMPAT, compat_setpwent, (void *)SETPWENT }, |
| 464 |
#ifdef NS_CACHING |
| 465 |
NS_CACHE_CB(&cache_info) |
| 466 |
#endif |
| 467 |
{ NULL, NULL, NULL } |
| 468 |
}; |
| 469 |
(void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc, |
| 470 |
stayopen); |
| 471 |
return (1); |
| 472 |
} |
| 473 |
|
| 474 |
|
| 475 |
void |
| 476 |
endpwent(void) |
| 477 |
{ |
| 478 |
#ifdef NS_CACHING |
| 479 |
static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( |
| 480 |
passwd, (void *)nss_lt_all, |
| 481 |
NULL, NULL); |
| 482 |
#endif |
| 483 |
|
| 484 |
static const ns_dtab dtab[] = { |
| 485 |
{ NSSRC_FILES, files_setpwent, (void *)ENDPWENT }, |
| 486 |
#ifdef HESIOD |
| 487 |
{ NSSRC_DNS, dns_setpwent, (void *)ENDPWENT }, |
| 488 |
#endif |
| 489 |
#ifdef YP |
| 490 |
{ NSSRC_NIS, nis_setpwent, (void *)ENDPWENT }, |
| 491 |
#endif |
| 492 |
{ NSSRC_COMPAT, compat_setpwent, (void *)ENDPWENT }, |
| 493 |
#ifdef NS_CACHING |
| 494 |
NS_CACHE_CB(&cache_info) |
| 495 |
#endif |
| 496 |
{ NULL, NULL, NULL } |
| 497 |
}; |
| 498 |
(void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "endpwent", defaultsrc); |
| 499 |
} |
| 500 |
|
| 501 |
|
| 502 |
int |
| 503 |
getpwent_r(struct passwd *pwd, char *buffer, size_t bufsize, |
| 504 |
struct passwd **result) |
| 505 |
{ |
| 506 |
#ifdef NS_CACHING |
| 507 |
static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( |
| 508 |
passwd, (void *)nss_lt_all, |
| 509 |
pwd_marshal_func, pwd_unmarshal_func); |
| 510 |
#endif |
| 511 |
|
| 512 |
static const ns_dtab dtab[] = { |
| 513 |
{ NSSRC_FILES, files_passwd, (void *)nss_lt_all }, |
| 514 |
#ifdef HESIOD |
| 515 |
{ NSSRC_DNS, dns_passwd, (void *)nss_lt_all }, |
| 516 |
#endif |
| 517 |
#ifdef YP |
| 518 |
{ NSSRC_NIS, nis_passwd, (void *)nss_lt_all }, |
| 519 |
#endif |
| 520 |
{ NSSRC_COMPAT, compat_passwd, (void *)nss_lt_all }, |
| 521 |
#ifdef NS_CACHING |
| 522 |
NS_CACHE_CB(&cache_info) |
| 523 |
#endif |
| 524 |
{ NULL, NULL, NULL } |
| 525 |
}; |
| 526 |
int rv, ret_errno; |
| 527 |
|
| 528 |
pwd_init(pwd); |
| 529 |
ret_errno = 0; |
| 530 |
*result = NULL; |
| 531 |
rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwent_r", defaultsrc, |
| 532 |
pwd, buffer, bufsize, &ret_errno); |
| 533 |
if (rv == NS_SUCCESS) |
| 534 |
return (0); |
| 535 |
else |
| 536 |
return (ret_errno); |
| 537 |
} |
| 538 |
|
| 539 |
|
| 540 |
int |
| 541 |
getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t bufsize, |
| 542 |
struct passwd **result) |
| 543 |
{ |
| 544 |
#ifdef NS_CACHING |
| 545 |
static const nss_cache_info cache_info = |
| 546 |
NS_COMMON_CACHE_INFO_INITIALIZER( |
| 547 |
passwd, (void *)nss_lt_name, |
| 548 |
pwd_id_func, pwd_marshal_func, pwd_unmarshal_func); |
| 549 |
#endif |
| 550 |
|
| 551 |
static const ns_dtab dtab[] = { |
| 552 |
{ NSSRC_FILES, files_passwd, (void *)nss_lt_name }, |
| 553 |
#ifdef HESIOD |
| 554 |
{ NSSRC_DNS, dns_passwd, (void *)nss_lt_name }, |
| 555 |
#endif |
| 556 |
#ifdef YP |
| 557 |
{ NSSRC_NIS, nis_passwd, (void *)nss_lt_name }, |
| 558 |
#endif |
| 559 |
{ NSSRC_COMPAT, compat_passwd, (void *)nss_lt_name }, |
| 560 |
#ifdef NS_CACHING |
| 561 |
NS_CACHE_CB(&cache_info) |
| 562 |
#endif |
| 563 |
{ NULL, NULL, NULL } |
| 564 |
}; |
| 565 |
int rv, ret_errno; |
| 566 |
|
| 567 |
pwd_init(pwd); |
| 568 |
ret_errno = 0; |
| 569 |
*result = NULL; |
| 570 |
rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwnam_r", defaultsrc, |
| 571 |
name, pwd, buffer, bufsize, &ret_errno); |
| 572 |
if (rv == NS_SUCCESS) |
| 573 |
return (0); |
| 574 |
else |
| 575 |
return (ret_errno); |
| 576 |
} |
| 577 |
|
| 578 |
|
| 579 |
int |
| 580 |
getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, |
| 581 |
struct passwd **result) |
| 582 |
{ |
| 583 |
#ifdef NS_CACHING |
| 584 |
static const nss_cache_info cache_info = |
| 585 |
NS_COMMON_CACHE_INFO_INITIALIZER( |
| 586 |
passwd, (void *)nss_lt_id, |
| 587 |
pwd_id_func, pwd_marshal_func, pwd_unmarshal_func); |
| 588 |
#endif |
| 589 |
|
| 590 |
static const ns_dtab dtab[] = { |
| 591 |
{ NSSRC_FILES, files_passwd, (void *)nss_lt_id }, |
| 592 |
#ifdef HESIOD |
| 593 |
{ NSSRC_DNS, dns_passwd, (void *)nss_lt_id }, |
| 594 |
#endif |
| 595 |
#ifdef YP |
| 596 |
{ NSSRC_NIS, nis_passwd, (void *)nss_lt_id }, |
| 597 |
#endif |
| 598 |
{ NSSRC_COMPAT, compat_passwd, (void *)nss_lt_id }, |
| 599 |
#ifdef NS_CACHING |
| 600 |
NS_CACHE_CB(&cache_info) |
| 601 |
#endif |
| 602 |
{ NULL, NULL, NULL } |
| 603 |
}; |
| 604 |
int rv, ret_errno; |
| 605 |
|
| 606 |
pwd_init(pwd); |
| 607 |
ret_errno = 0; |
| 608 |
*result = NULL; |
| 609 |
rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwuid_r", defaultsrc, |
| 610 |
uid, pwd, buffer, bufsize, &ret_errno); |
| 611 |
if (rv == NS_SUCCESS) |
| 612 |
return (0); |
| 613 |
else |
| 614 |
return (ret_errno); |
| 615 |
} |
| 616 |
|
| 617 |
|
| 618 |
static void |
| 619 |
pwd_init(struct passwd *pwd) |
| 620 |
{ |
| 621 |
static char nul[] = ""; |
| 622 |
|
| 623 |
memset(pwd, 0, sizeof(*pwd)); |
| 624 |
pwd->pw_uid = (uid_t)-1; /* Considered least likely to lead to */ |
| 625 |
pwd->pw_gid = (gid_t)-1; /* a security issue. */ |
| 626 |
pwd->pw_name = nul; |
| 627 |
pwd->pw_passwd = nul; |
| 628 |
pwd->pw_class = nul; |
| 629 |
pwd->pw_gecos = nul; |
| 630 |
pwd->pw_dir = nul; |
| 631 |
pwd->pw_shell = nul; |
| 632 |
} |
| 633 |
|
| 634 |
|
| 635 |
static struct passwd pwd; |
| 636 |
static char *pwd_storage; |
| 637 |
static size_t pwd_storage_size; |
| 638 |
|
| 639 |
|
| 640 |
static struct passwd * |
| 641 |
getpw(int (*fn)(union key, struct passwd *, char *, size_t, struct passwd **), |
| 642 |
union key key) |
| 643 |
{ |
| 644 |
int rv; |
| 645 |
struct passwd *res; |
| 646 |
|
| 647 |
if (pwd_storage == NULL) { |
| 648 |
pwd_storage = malloc(PWD_STORAGE_INITIAL); |
| 649 |
if (pwd_storage == NULL) |
| 650 |
return (NULL); |
| 651 |
pwd_storage_size = PWD_STORAGE_INITIAL; |
| 652 |
} |
| 653 |
do { |
| 654 |
rv = fn(key, &pwd, pwd_storage, pwd_storage_size, &res); |
| 655 |
if (res == NULL && rv == ERANGE) { |
| 656 |
free(pwd_storage); |
| 657 |
if ((pwd_storage_size << 1) > PWD_STORAGE_MAX) { |
| 658 |
pwd_storage = NULL; |
| 659 |
errno = ERANGE; |
| 660 |
return (NULL); |
| 661 |
} |
| 662 |
pwd_storage_size <<= 1; |
| 663 |
pwd_storage = malloc(pwd_storage_size); |
| 664 |
if (pwd_storage == NULL) |
| 665 |
return (NULL); |
| 666 |
} |
| 667 |
} while (res == NULL && rv == ERANGE); |
| 668 |
if (rv != 0) |
| 669 |
errno = rv; |
| 670 |
return (res); |
| 671 |
} |
| 672 |
|
| 673 |
|
| 674 |
static int |
| 675 |
wrap_getpwnam_r(union key key, struct passwd *pwd, char *buffer, |
| 676 |
size_t bufsize, struct passwd **res) |
| 677 |
{ |
| 678 |
return (getpwnam_r(key.name, pwd, buffer, bufsize, res)); |
| 679 |
} |
| 680 |
|
| 681 |
|
| 682 |
static int |
| 683 |
wrap_getpwuid_r(union key key, struct passwd *pwd, char *buffer, |
| 684 |
size_t bufsize, struct passwd **res) |
| 685 |
{ |
| 686 |
return (getpwuid_r(key.uid, pwd, buffer, bufsize, res)); |
| 687 |
} |
| 688 |
|
| 689 |
|
| 690 |
static int |
| 691 |
wrap_getpwent_r(union key key __unused, struct passwd *pwd, char *buffer, |
| 692 |
size_t bufsize, struct passwd **res) |
| 693 |
{ |
| 694 |
return (getpwent_r(pwd, buffer, bufsize, res)); |
| 695 |
} |
| 696 |
|
| 697 |
|
| 698 |
struct passwd * |
| 699 |
getpwnam(const char *name) |
| 700 |
{ |
| 701 |
union key key; |
| 702 |
|
| 703 |
key.name = name; |
| 704 |
return (getpw(wrap_getpwnam_r, key)); |
| 705 |
} |
| 706 |
|
| 707 |
|
| 708 |
struct passwd * |
| 709 |
getpwuid(uid_t uid) |
| 710 |
{ |
| 711 |
union key key; |
| 712 |
|
| 713 |
key.uid = uid; |
| 714 |
return (getpw(wrap_getpwuid_r, key)); |
| 715 |
} |
| 716 |
|
| 717 |
|
| 718 |
struct passwd * |
| 719 |
getpwent(void) |
| 720 |
{ |
| 721 |
union key key; |
| 722 |
|
| 723 |
key.uid = 0; /* not used */ |
| 724 |
return (getpw(wrap_getpwent_r, key)); |
| 725 |
} |
| 726 |
|
| 727 |
|
| 728 |
/* |
| 729 |
* files backend |
| 730 |
*/ |
| 731 |
static DB * |
| 732 |
pwdbopen(int *version) |
| 733 |
{ |
| 734 |
DB *res; |
| 735 |
DBT key, entry; |
| 736 |
int rv; |
| 737 |
|
| 738 |
if (geteuid() != 0 || |
| 739 |
(res = dbopen(_PATH_SMP_DB, O_RDONLY, 0, DB_HASH, NULL)) == NULL) |
| 740 |
res = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL); |
| 741 |
if (res == NULL) |
| 742 |
return (NULL); |
| 743 |
key.data = _PWD_VERSION_KEY; |
| 744 |
key.size = strlen(_PWD_VERSION_KEY); |
| 745 |
rv = res->get(res, &key, &entry, 0); |
| 746 |
if (rv == 0) |
| 747 |
*version = *(unsigned char *)entry.data; |
| 748 |
else |
| 749 |
*version = 3; |
| 750 |
if (*version < 3 || |
| 751 |
*version >= nitems(pwdb_versions)) { |
| 752 |
syslog(LOG_CRIT, "Unsupported password database version %d", |
| 753 |
*version); |
| 754 |
res->close(res); |
| 755 |
res = NULL; |
| 756 |
} |
| 757 |
return (res); |
| 758 |
} |
| 759 |
|
| 760 |
|
| 761 |
static void |
| 762 |
files_endstate(void *p) |
| 763 |
{ |
| 764 |
DB *db; |
| 765 |
|
| 766 |
if (p == NULL) |
| 767 |
return; |
| 768 |
db = ((struct files_state *)p)->db; |
| 769 |
if (db != NULL) |
| 770 |
db->close(db); |
| 771 |
free(p); |
| 772 |
} |
| 773 |
|
| 774 |
|
| 775 |
static int |
| 776 |
files_setpwent(void *retval, void *mdata, va_list ap) |
| 777 |
{ |
| 778 |
struct files_state *st; |
| 779 |
int rv, stayopen; |
| 780 |
|
| 781 |
rv = files_getstate(&st); |
| 782 |
if (rv != 0) |
| 783 |
return (NS_UNAVAIL); |
| 784 |
switch ((enum constants)mdata) { |
| 785 |
case SETPWENT: |
| 786 |
stayopen = va_arg(ap, int); |
| 787 |
st->keynum = 0; |
| 788 |
if (stayopen) |
| 789 |
st->db = pwdbopen(&st->version); |
| 790 |
st->stayopen = stayopen; |
| 791 |
break; |
| 792 |
case ENDPWENT: |
| 793 |
if (st->db != NULL) { |
| 794 |
(void)st->db->close(st->db); |
| 795 |
st->db = NULL; |
| 796 |
} |
| 797 |
break; |
| 798 |
default: |
| 799 |
break; |
| 800 |
} |
| 801 |
return (NS_UNAVAIL); |
| 802 |
} |
| 803 |
|
| 804 |
|
| 805 |
static int |
| 806 |
files_passwd(void *retval, void *mdata, va_list ap) |
| 807 |
{ |
| 808 |
char keybuf[MAXLOGNAME + 1]; |
| 809 |
DBT key, entry; |
| 810 |
struct files_state *st; |
| 811 |
enum nss_lookup_type how; |
| 812 |
const char *name; |
| 813 |
struct passwd *pwd; |
| 814 |
char *buffer; |
| 815 |
size_t bufsize, namesize; |
| 816 |
uid_t uid; |
| 817 |
uint32_t store; |
| 818 |
int rv, stayopen = 0, *errnop; |
| 819 |
|
| 820 |
name = NULL; |
| 821 |
uid = (uid_t)-1; |
| 822 |
how = (enum nss_lookup_type)mdata; |
| 823 |
switch (how) { |
| 824 |
case nss_lt_name: |
| 825 |
name = va_arg(ap, const char *); |
| 826 |
keybuf[0] = _PW_KEYBYNAME; |
| 827 |
break; |
| 828 |
case nss_lt_id: |
| 829 |
uid = va_arg(ap, uid_t); |
| 830 |
keybuf[0] = _PW_KEYBYUID; |
| 831 |
break; |
| 832 |
case nss_lt_all: |
| 833 |
keybuf[0] = _PW_KEYBYNUM; |
| 834 |
break; |
| 835 |
default: |
| 836 |
rv = NS_NOTFOUND; |
| 837 |
goto fin; |
| 838 |
} |
| 839 |
pwd = va_arg(ap, struct passwd *); |
| 840 |
buffer = va_arg(ap, char *); |
| 841 |
bufsize = va_arg(ap, size_t); |
| 842 |
errnop = va_arg(ap, int *); |
| 843 |
*errnop = files_getstate(&st); |
| 844 |
if (*errnop != 0) |
| 845 |
return (NS_UNAVAIL); |
| 846 |
if (how == nss_lt_all && st->keynum < 0) { |
| 847 |
rv = NS_NOTFOUND; |
| 848 |
goto fin; |
| 849 |
} |
| 850 |
if (st->db == NULL && |
| 851 |
(st->db = pwdbopen(&st->version)) == NULL) { |
| 852 |
*errnop = errno; |
| 853 |
rv = NS_UNAVAIL; |
| 854 |
goto fin; |
| 855 |
} |
| 856 |
if (how == nss_lt_all) |
| 857 |
stayopen = 1; |
| 858 |
else |
| 859 |
stayopen = st->stayopen; |
| 860 |
key.data = keybuf; |
| 861 |
do { |
| 862 |
switch (how) { |
| 863 |
case nss_lt_name: |
| 864 |
/* MAXLOGNAME includes NUL byte, but we do not |
| 865 |
* include the NUL byte in the key. |
| 866 |
*/ |
| 867 |
namesize = strlcpy(&keybuf[1], name, sizeof(keybuf)-1); |
| 868 |
if (namesize >= sizeof(keybuf)-1) { |
| 869 |
*errnop = EINVAL; |
| 870 |
rv = NS_NOTFOUND; |
| 871 |
goto fin; |
| 872 |
} |
| 873 |
key.size = namesize + 1; |
| 874 |
break; |
| 875 |
case nss_lt_id: |
| 876 |
if (st->version < _PWD_CURRENT_VERSION) { |
| 877 |
memcpy(&keybuf[1], &uid, sizeof(uid)); |
| 878 |
key.size = sizeof(uid) + 1; |
| 879 |
} else { |
| 880 |
store = htonl(uid); |
| 881 |
memcpy(&keybuf[1], &store, sizeof(store)); |
| 882 |
key.size = sizeof(store) + 1; |
| 883 |
} |
| 884 |
break; |
| 885 |
case nss_lt_all: |
| 886 |
st->keynum++; |
| 887 |
if (st->version < _PWD_CURRENT_VERSION) { |
| 888 |
memcpy(&keybuf[1], &st->keynum, |
| 889 |
sizeof(st->keynum)); |
| 890 |
key.size = sizeof(st->keynum) + 1; |
| 891 |
} else { |
| 892 |
store = htonl(st->keynum); |
| 893 |
memcpy(&keybuf[1], &store, sizeof(store)); |
| 894 |
key.size = sizeof(store) + 1; |
| 895 |
} |
| 896 |
break; |
| 897 |
} |
| 898 |
keybuf[0] = _PW_VERSIONED(keybuf[0], st->version); |
| 899 |
rv = st->db->get(st->db, &key, &entry, 0); |
| 900 |
if (rv < 0 || rv > 1) { /* should never return > 1 */ |
| 901 |
*errnop = errno; |
| 902 |
rv = NS_UNAVAIL; |
| 903 |
goto fin; |
| 904 |
} else if (rv == 1) { |
| 905 |
if (how == nss_lt_all) |
| 906 |
st->keynum = -1; |
| 907 |
rv = NS_NOTFOUND; |
| 908 |
goto fin; |
| 909 |
} |
| 910 |
rv = pwdb_versions[st->version].match(entry.data, entry.size, |
| 911 |
how, name, uid); |
| 912 |
if (rv != NS_SUCCESS) |
| 913 |
continue; |
| 914 |
if (entry.size > bufsize) { |
| 915 |
*errnop = ERANGE; |
| 916 |
rv = NS_RETURN; |
| 917 |
break; |
| 918 |
} |
| 919 |
memcpy(buffer, entry.data, entry.size); |
| 920 |
rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd, |
| 921 |
errnop); |
| 922 |
} while (how == nss_lt_all && !(rv & NS_TERMINATE)); |
| 923 |
fin: |
| 924 |
if (st->db != NULL && !stayopen) { |
| 925 |
(void)st->db->close(st->db); |
| 926 |
st->db = NULL; |
| 927 |
} |
| 928 |
if (rv == NS_SUCCESS) { |
| 929 |
pwd->pw_fields &= ~_PWF_SOURCE; |
| 930 |
pwd->pw_fields |= _PWF_FILES; |
| 931 |
if (retval != NULL) |
| 932 |
*(struct passwd **)retval = pwd; |
| 933 |
} |
| 934 |
return (rv); |
| 935 |
} |
| 936 |
|
| 937 |
|
| 938 |
static int |
| 939 |
pwdb_match_entry_v3(char *entry, size_t entrysize, enum nss_lookup_type how, |
| 940 |
const char *name, uid_t uid) |
| 941 |
{ |
| 942 |
const char *p, *eom; |
| 943 |
uid_t uid2; |
| 944 |
|
| 945 |
eom = &entry[entrysize]; |
| 946 |
for (p = entry; p < eom; p++) |
| 947 |
if (*p == '\0') |
| 948 |
break; |
| 949 |
if (*p != '\0') |
| 950 |
return (NS_NOTFOUND); |
| 951 |
if (how == nss_lt_all) |
| 952 |
return (NS_SUCCESS); |
| 953 |
if (how == nss_lt_name) |
| 954 |
return (strcmp(name, entry) == 0 ? NS_SUCCESS : NS_NOTFOUND); |
| 955 |
for (p++; p < eom; p++) |
| 956 |
if (*p == '\0') |
| 957 |
break; |
| 958 |
if (*p != '\0' || (++p) + sizeof(uid) >= eom) |
| 959 |
return (NS_NOTFOUND); |
| 960 |
memcpy(&uid2, p, sizeof(uid2)); |
| 961 |
return (uid == uid2 ? NS_SUCCESS : NS_NOTFOUND); |
| 962 |
} |
| 963 |
|
| 964 |
|
| 965 |
static int |
| 966 |
pwdb_parse_entry_v3(char *buffer, size_t bufsize, struct passwd *pwd, |
| 967 |
int *errnop) |
| 968 |
{ |
| 969 |
char *p, *eom; |
| 970 |
int32_t pw_change, pw_expire; |
| 971 |
|
| 972 |
/* THIS CODE MUST MATCH THAT IN pwd_mkdb. */ |
| 973 |
p = buffer; |
| 974 |
eom = &buffer[bufsize]; |
| 975 |
#define STRING(field) do { \ |
| 976 |
(field) = p; \ |
| 977 |
while (p < eom && *p != '\0') \ |
| 978 |
p++; \ |
| 979 |
if (p >= eom) \ |
| 980 |
return (NS_NOTFOUND); \ |
| 981 |
p++; \ |
| 982 |
} while (0) |
| 983 |
#define SCALAR(field) do { \ |
| 984 |
if (p + sizeof(field) > eom) \ |
| 985 |
return (NS_NOTFOUND); \ |
| 986 |
memcpy(&(field), p, sizeof(field)); \ |
| 987 |
p += sizeof(field); \ |
| 988 |
} while (0) |
| 989 |
STRING(pwd->pw_name); |
| 990 |
STRING(pwd->pw_passwd); |
| 991 |
SCALAR(pwd->pw_uid); |
| 992 |
SCALAR(pwd->pw_gid); |
| 993 |
SCALAR(pw_change); |
| 994 |
STRING(pwd->pw_class); |
| 995 |
STRING(pwd->pw_gecos); |
| 996 |
STRING(pwd->pw_dir); |
| 997 |
STRING(pwd->pw_shell); |
| 998 |
SCALAR(pw_expire); |
| 999 |
SCALAR(pwd->pw_fields); |
| 1000 |
#undef STRING |
| 1001 |
#undef SCALAR |
| 1002 |
pwd->pw_change = pw_change; |
| 1003 |
pwd->pw_expire = pw_expire; |
| 1004 |
return (NS_SUCCESS); |
| 1005 |
} |
| 1006 |
|
| 1007 |
|
| 1008 |
static int |
| 1009 |
pwdb_match_entry_v4(char *entry, size_t entrysize, enum nss_lookup_type how, |
| 1010 |
const char *name, uid_t uid) |
| 1011 |
{ |
| 1012 |
const char *p, *eom; |
| 1013 |
uint32_t uid2; |
| 1014 |
|
| 1015 |
eom = &entry[entrysize]; |
| 1016 |
for (p = entry; p < eom; p++) |
| 1017 |
if (*p == '\0') |
| 1018 |
break; |
| 1019 |
if (*p != '\0') |
| 1020 |
return (NS_NOTFOUND); |
| 1021 |
if (how == nss_lt_all) |
| 1022 |
return (NS_SUCCESS); |
| 1023 |
if (how == nss_lt_name) |
| 1024 |
return (strcmp(name, entry) == 0 ? NS_SUCCESS : NS_NOTFOUND); |
| 1025 |
for (p++; p < eom; p++) |
| 1026 |
if (*p == '\0') |
| 1027 |
break; |
| 1028 |
if (*p != '\0' || (++p) + sizeof(uid) >= eom) |
| 1029 |
return (NS_NOTFOUND); |
| 1030 |
memcpy(&uid2, p, sizeof(uid2)); |
| 1031 |
uid2 = ntohl(uid2); |
| 1032 |
return (uid == (uid_t)uid2 ? NS_SUCCESS : NS_NOTFOUND); |
| 1033 |
} |
| 1034 |
|
| 1035 |
|
| 1036 |
static int |
| 1037 |
pwdb_parse_entry_v4(char *buffer, size_t bufsize, struct passwd *pwd, |
| 1038 |
int *errnop) |
| 1039 |
{ |
| 1040 |
char *p, *eom; |
| 1041 |
uint32_t n; |
| 1042 |
|
| 1043 |
/* THIS CODE MUST MATCH THAT IN pwd_mkdb. */ |
| 1044 |
p = buffer; |
| 1045 |
eom = &buffer[bufsize]; |
| 1046 |
#define STRING(field) do { \ |
| 1047 |
(field) = p; \ |
| 1048 |
while (p < eom && *p != '\0') \ |
| 1049 |
p++; \ |
| 1050 |
if (p >= eom) \ |
| 1051 |
return (NS_NOTFOUND); \ |
| 1052 |
p++; \ |
| 1053 |
} while (0) |
| 1054 |
#define SCALAR(field) do { \ |
| 1055 |
if (p + sizeof(n) > eom) \ |
| 1056 |
return (NS_NOTFOUND); \ |
| 1057 |
memcpy(&n, p, sizeof(n)); \ |
| 1058 |
(field) = ntohl(n); \ |
| 1059 |
p += sizeof(n); \ |
| 1060 |
} while (0) |
| 1061 |
STRING(pwd->pw_name); |
| 1062 |
STRING(pwd->pw_passwd); |
| 1063 |
SCALAR(pwd->pw_uid); |
| 1064 |
SCALAR(pwd->pw_gid); |
| 1065 |
SCALAR(pwd->pw_change); |
| 1066 |
STRING(pwd->pw_class); |
| 1067 |
STRING(pwd->pw_gecos); |
| 1068 |
STRING(pwd->pw_dir); |
| 1069 |
STRING(pwd->pw_shell); |
| 1070 |
SCALAR(pwd->pw_expire); |
| 1071 |
SCALAR(pwd->pw_fields); |
| 1072 |
#undef STRING |
| 1073 |
#undef SCALAR |
| 1074 |
return (NS_SUCCESS); |
| 1075 |
} |
| 1076 |
|
| 1077 |
|
| 1078 |
#ifdef HESIOD |
| 1079 |
/* |
| 1080 |
* dns backend |
| 1081 |
*/ |
| 1082 |
static void |
| 1083 |
dns_endstate(void *p) |
| 1084 |
{ |
| 1085 |
free(p); |
| 1086 |
} |
| 1087 |
|
| 1088 |
|
| 1089 |
static int |
| 1090 |
dns_setpwent(void *retval, void *mdata, va_list ap) |
| 1091 |
{ |
| 1092 |
struct dns_state *st; |
| 1093 |
int rv; |
| 1094 |
|
| 1095 |
rv = dns_getstate(&st); |
| 1096 |
if (rv != 0) |
| 1097 |
return (NS_UNAVAIL); |
| 1098 |
st->counter = 0; |
| 1099 |
return (NS_UNAVAIL); |
| 1100 |
} |
| 1101 |
|
| 1102 |
|
| 1103 |
static int |
| 1104 |
dns_passwd(void *retval, void *mdata, va_list ap) |
| 1105 |
{ |
| 1106 |
char buf[HESIOD_NAME_MAX]; |
| 1107 |
struct dns_state *st; |
| 1108 |
struct passwd *pwd; |
| 1109 |
const char *name, *label; |
| 1110 |
void *ctx; |
| 1111 |
char *buffer, **hes; |
| 1112 |
size_t bufsize, linesize; |
| 1113 |
uid_t uid; |
| 1114 |
enum nss_lookup_type how; |
| 1115 |
int rv, *errnop; |
| 1116 |
|
| 1117 |
ctx = NULL; |
| 1118 |
hes = NULL; |
| 1119 |
name = NULL; |
| 1120 |
uid = (uid_t)-1; |
| 1121 |
how = (enum nss_lookup_type)mdata; |
| 1122 |
switch (how) { |
| 1123 |
case nss_lt_name: |
| 1124 |
name = va_arg(ap, const char *); |
| 1125 |
break; |
| 1126 |
case nss_lt_id: |
| 1127 |
uid = va_arg(ap, uid_t); |
| 1128 |
break; |
| 1129 |
case nss_lt_all: |
| 1130 |
break; |
| 1131 |
} |
| 1132 |
pwd = va_arg(ap, struct passwd *); |
| 1133 |
buffer = va_arg(ap, char *); |
| 1134 |
bufsize = va_arg(ap, size_t); |
| 1135 |
errnop = va_arg(ap, int *); |
| 1136 |
*errnop = dns_getstate(&st); |
| 1137 |
if (*errnop != 0) |
| 1138 |
return (NS_UNAVAIL); |
| 1139 |
if (hesiod_init(&ctx) != 0) { |
| 1140 |
*errnop = errno; |
| 1141 |
rv = NS_UNAVAIL; |
| 1142 |
goto fin; |
| 1143 |
} |
| 1144 |
do { |
| 1145 |
rv = NS_NOTFOUND; |
| 1146 |
switch (how) { |
| 1147 |
case nss_lt_name: |
| 1148 |
label = name; |
| 1149 |
break; |
| 1150 |
case nss_lt_id: |
| 1151 |
if (snprintf(buf, sizeof(buf), "%lu", |
| 1152 |
(unsigned long)uid) >= sizeof(buf)) |
| 1153 |
goto fin; |
| 1154 |
label = buf; |
| 1155 |
break; |
| 1156 |
case nss_lt_all: |
| 1157 |
if (st->counter < 0) |
| 1158 |
goto fin; |
| 1159 |
if (snprintf(buf, sizeof(buf), "passwd-%ld", |
| 1160 |
st->counter++) >= sizeof(buf)) |
| 1161 |
goto fin; |
| 1162 |
label = buf; |
| 1163 |
break; |
| 1164 |
} |
| 1165 |
hes = hesiod_resolve(ctx, label, |
| 1166 |
how == nss_lt_id ? "uid" : "passwd"); |
| 1167 |
if (hes == NULL) { |
| 1168 |
if (how == nss_lt_all) |
| 1169 |
st->counter = -1; |
| 1170 |
if (errno != ENOENT) |
| 1171 |
*errnop = errno; |
| 1172 |
goto fin; |
| 1173 |
} |
| 1174 |
rv = __pw_match_entry(hes[0], strlen(hes[0]), how, name, uid); |
| 1175 |
if (rv != NS_SUCCESS) { |
| 1176 |
hesiod_free_list(ctx, hes); |
| 1177 |
hes = NULL; |
| 1178 |
continue; |
| 1179 |
} |
| 1180 |
linesize = strlcpy(buffer, hes[0], bufsize); |
| 1181 |
if (linesize >= bufsize) { |
| 1182 |
*errnop = ERANGE; |
| 1183 |
rv = NS_RETURN; |
| 1184 |
continue; |
| 1185 |
} |
| 1186 |
hesiod_free_list(ctx, hes); |
| 1187 |
hes = NULL; |
| 1188 |
rv = __pw_parse_entry(buffer, bufsize, pwd, 0, errnop); |
| 1189 |
} while (how == nss_lt_all && !(rv & NS_TERMINATE)); |
| 1190 |
fin: |
| 1191 |
if (hes != NULL) |
| 1192 |
hesiod_free_list(ctx, hes); |
| 1193 |
if (ctx != NULL) |
| 1194 |
hesiod_end(ctx); |
| 1195 |
if (rv == NS_SUCCESS) { |
| 1196 |
pwd->pw_fields &= ~_PWF_SOURCE; |
| 1197 |
pwd->pw_fields |= _PWF_HESIOD; |
| 1198 |
if (retval != NULL) |
| 1199 |
*(struct passwd **)retval = pwd; |
| 1200 |
} |
| 1201 |
return (rv); |
| 1202 |
} |
| 1203 |
#endif /* HESIOD */ |
| 1204 |
|
| 1205 |
|
| 1206 |
#ifdef YP |
| 1207 |
/* |
| 1208 |
* nis backend |
| 1209 |
*/ |
| 1210 |
static void |
| 1211 |
nis_endstate(void *p) |
| 1212 |
{ |
| 1213 |
free(((struct nis_state *)p)->key); |
| 1214 |
free(p); |
| 1215 |
} |
| 1216 |
|
| 1217 |
/* |
| 1218 |
* Test for the presence of special FreeBSD-specific master.passwd.by* |
| 1219 |
* maps. We do this using yp_order(). If it fails, then either the server |
| 1220 |
* doesn't have the map, or the YPPROC_ORDER procedure isn't supported by |
| 1221 |
* the server (Sun NIS+ servers in YP compat mode behave this way). If |
| 1222 |
* the master.passwd.by* maps don't exist, then let the lookup routine try |
| 1223 |
* the regular passwd.by* maps instead. If the lookup routine fails, it |
| 1224 |
* can return an error as needed. |
| 1225 |
*/ |
| 1226 |
static int |
| 1227 |
nis_map(char *domain, enum nss_lookup_type how, char *buffer, size_t bufsize, |
| 1228 |
int *master) |
| 1229 |
{ |
| 1230 |
int rv, order; |
| 1231 |
|
| 1232 |
*master = 0; |
| 1233 |
if (geteuid() == 0) { |
| 1234 |
if (snprintf(buffer, bufsize, "master.passwd.by%s", |
| 1235 |
(how == nss_lt_id) ? "uid" : "name") >= bufsize) |
| 1236 |
return (NS_UNAVAIL); |
| 1237 |
rv = yp_order(domain, buffer, &order); |
| 1238 |
if (rv == 0) { |
| 1239 |
*master = 1; |
| 1240 |
return (NS_SUCCESS); |
| 1241 |
} |
| 1242 |
} |
| 1243 |
|
| 1244 |
if (snprintf(buffer, bufsize, "passwd.by%s", |
| 1245 |
(how == nss_lt_id) ? "uid" : "name") >= bufsize) |
| 1246 |
return (NS_UNAVAIL); |
| 1247 |
|
| 1248 |
return (NS_SUCCESS); |
| 1249 |
} |
| 1250 |
|
| 1251 |
|
| 1252 |
static int |
| 1253 |
nis_adjunct(char *domain, const char *name, char *buffer, size_t bufsize) |
| 1254 |
{ |
| 1255 |
int rv; |
| 1256 |
char *result, *p, *q, *eor; |
| 1257 |
int resultlen; |
| 1258 |
|
| 1259 |
result = NULL; |
| 1260 |
rv = yp_match(domain, "passwd.adjunct.byname", name, strlen(name), |
| 1261 |
&result, &resultlen); |
| 1262 |
if (rv != 0) |
| 1263 |
rv = 1; |
| 1264 |
else { |
| 1265 |
eor = &result[resultlen]; |
| 1266 |
p = memchr(result, ':', eor - result); |
| 1267 |
if (p != NULL && ++p < eor && |
| 1268 |
(q = memchr(p, ':', eor - p)) != NULL) { |
| 1269 |
if (q - p >= bufsize) |
| 1270 |
rv = -1; |
| 1271 |
else { |
| 1272 |
memcpy(buffer, p, q - p); |
| 1273 |
buffer[q - p] ='\0'; |
| 1274 |
} |
| 1275 |
} else |
| 1276 |
rv = 1; |
| 1277 |
} |
| 1278 |
free(result); |
| 1279 |
return (rv); |
| 1280 |
} |
| 1281 |
|
| 1282 |
|
| 1283 |
static int |
| 1284 |
nis_setpwent(void *retval, void *mdata, va_list ap) |
| 1285 |
{ |
| 1286 |
struct nis_state *st; |
| 1287 |
int rv; |
| 1288 |
|
| 1289 |
rv = nis_getstate(&st); |
| 1290 |
if (rv != 0) |
| 1291 |
return (NS_UNAVAIL); |
| 1292 |
st->done = 0; |
| 1293 |
free(st->key); |
| 1294 |
st->key = NULL; |
| 1295 |
return (NS_UNAVAIL); |
| 1296 |
} |
| 1297 |
|
| 1298 |
|
| 1299 |
static int |
| 1300 |
nis_passwd(void *retval, void *mdata, va_list ap) |
| 1301 |
{ |
| 1302 |
char map[YPMAXMAP]; |
| 1303 |
struct nis_state *st; |
| 1304 |
struct passwd *pwd; |
| 1305 |
const char *name; |
| 1306 |
char *buffer, *key, *result; |
| 1307 |
size_t bufsize; |
| 1308 |
uid_t uid; |
| 1309 |
enum nss_lookup_type how; |
| 1310 |
int *errnop, keylen, resultlen, rv, master; |
| 1311 |
|
| 1312 |
name = NULL; |
| 1313 |
uid = (uid_t)-1; |
| 1314 |
how = (enum nss_lookup_type)mdata; |
| 1315 |
switch (how) { |
| 1316 |
case nss_lt_name: |
| 1317 |
name = va_arg(ap, const char *); |
| 1318 |
break; |
| 1319 |
case nss_lt_id: |
| 1320 |
uid = va_arg(ap, uid_t); |
| 1321 |
break; |
| 1322 |
case nss_lt_all: |
| 1323 |
break; |
| 1324 |
} |
| 1325 |
pwd = va_arg(ap, struct passwd *); |
| 1326 |
buffer = va_arg(ap, char *); |
| 1327 |
bufsize = va_arg(ap, size_t); |
| 1328 |
errnop = va_arg(ap, int *); |
| 1329 |
*errnop = nis_getstate(&st); |
| 1330 |
if (*errnop != 0) |
| 1331 |
return (NS_UNAVAIL); |
| 1332 |
if (st->domain[0] == '\0') { |
| 1333 |
if (getdomainname(st->domain, sizeof(st->domain)) != 0) { |
| 1334 |
*errnop = errno; |
| 1335 |
return (NS_UNAVAIL); |
| 1336 |
} |
| 1337 |
} |
| 1338 |
rv = nis_map(st->domain, how, map, sizeof(map), &master); |
| 1339 |
if (rv != NS_SUCCESS) |
| 1340 |
return (rv); |
| 1341 |
result = NULL; |
| 1342 |
do { |
| 1343 |
rv = NS_NOTFOUND; |
| 1344 |
switch (how) { |
| 1345 |
case nss_lt_name: |
| 1346 |
if (strlcpy(buffer, name, bufsize) >= bufsize) |
| 1347 |
goto erange; |
| 1348 |
break; |
| 1349 |
case nss_lt_id: |
| 1350 |
if (snprintf(buffer, bufsize, "%lu", |
| 1351 |
(unsigned long)uid) >= bufsize) |
| 1352 |
goto erange; |
| 1353 |
break; |
| 1354 |
case nss_lt_all: |
| 1355 |
if (st->done) |
| 1356 |
goto fin; |
| 1357 |
break; |
| 1358 |
} |
| 1359 |
result = NULL; |
| 1360 |
if (how == nss_lt_all) { |
| 1361 |
if (st->key == NULL) |
| 1362 |
rv = yp_first(st->domain, map, &st->key, |
| 1363 |
&st->keylen, &result, &resultlen); |
| 1364 |
else { |
| 1365 |
key = st->key; |
| 1366 |
keylen = st->keylen; |
| 1367 |
st->key = NULL; |
| 1368 |
rv = yp_next(st->domain, map, key, keylen, |
| 1369 |
&st->key, &st->keylen, &result, |
| 1370 |
&resultlen); |
| 1371 |
free(key); |
| 1372 |
} |
| 1373 |
if (rv != 0) { |
| 1374 |
free(result); |
| 1375 |
free(st->key); |
| 1376 |
st->key = NULL; |
| 1377 |
if (rv == YPERR_NOMORE) |
| 1378 |
st->done = 1; |
| 1379 |
else |
| 1380 |
rv = NS_UNAVAIL; |
| 1381 |
goto fin; |
| 1382 |
} |
| 1383 |
} else { |
| 1384 |
rv = yp_match(st->domain, map, buffer, strlen(buffer), |
| 1385 |
&result, &resultlen); |
| 1386 |
if (rv == YPERR_KEY) { |
| 1387 |
rv = NS_NOTFOUND; |
| 1388 |
continue; |
| 1389 |
} else if (rv != 0) { |
| 1390 |
free(result); |
| 1391 |
rv = NS_UNAVAIL; |
| 1392 |
continue; |
| 1393 |
} |
| 1394 |
} |
| 1395 |
if (resultlen >= bufsize) { |
| 1396 |
free(result); |
| 1397 |
goto erange; |
| 1398 |
} |
| 1399 |
memcpy(buffer, result, resultlen); |
| 1400 |
buffer[resultlen] = '\0'; |
| 1401 |
free(result); |
| 1402 |
rv = __pw_match_entry(buffer, resultlen, how, name, uid); |
| 1403 |
if (rv == NS_SUCCESS) |
| 1404 |
rv = __pw_parse_entry(buffer, resultlen, pwd, master, |
| 1405 |
errnop); |
| 1406 |
} while (how == nss_lt_all && !(rv & NS_TERMINATE)); |
| 1407 |
fin: |
| 1408 |
if (rv == NS_SUCCESS) { |
| 1409 |
if (strstr(pwd->pw_passwd, "##") != NULL) { |
| 1410 |
rv = nis_adjunct(st->domain, pwd->pw_name, |
| 1411 |
&buffer[resultlen+1], bufsize-resultlen-1); |
| 1412 |
if (rv < 0) |
| 1413 |
goto erange; |
| 1414 |
else if (rv == 0) |
| 1415 |
pwd->pw_passwd = &buffer[resultlen+1]; |
| 1416 |
} |
| 1417 |
pwd->pw_fields &= ~_PWF_SOURCE; |
| 1418 |
pwd->pw_fields |= _PWF_NIS; |
| 1419 |
if (retval != NULL) |
| 1420 |
*(struct passwd **)retval = pwd; |
| 1421 |
rv = NS_SUCCESS; |
| 1422 |
} |
| 1423 |
return (rv); |
| 1424 |
erange: |
| 1425 |
*errnop = ERANGE; |
| 1426 |
return (NS_RETURN); |
| 1427 |
} |
| 1428 |
#endif /* YP */ |
| 1429 |
|
| 1430 |
|
| 1431 |
/* |
| 1432 |
* compat backend |
| 1433 |
*/ |
| 1434 |
static void |
| 1435 |
compat_clear_template(struct passwd *template) |
| 1436 |
{ |
| 1437 |
|
| 1438 |
free(template->pw_passwd); |
| 1439 |
free(template->pw_gecos); |
| 1440 |
free(template->pw_dir); |
| 1441 |
free(template->pw_shell); |
| 1442 |
memset(template, 0, sizeof(*template)); |
| 1443 |
} |
| 1444 |
|
| 1445 |
|
| 1446 |
static int |
| 1447 |
compat_set_template(struct passwd *src, struct passwd *template) |
| 1448 |
{ |
| 1449 |
|
| 1450 |
compat_clear_template(template); |
| 1451 |
#ifdef PW_OVERRIDE_PASSWD |
| 1452 |
if ((src->pw_fields & _PWF_PASSWD) && |
| 1453 |
(template->pw_passwd = strdup(src->pw_passwd)) == NULL) |
| 1454 |
goto enomem; |
| 1455 |
#endif |
| 1456 |
if (src->pw_fields & _PWF_UID) |
| 1457 |
template->pw_uid = src->pw_uid; |
| 1458 |
if (src->pw_fields & _PWF_GID) |
| 1459 |
template->pw_gid = src->pw_gid; |
| 1460 |
if ((src->pw_fields & _PWF_GECOS) && |
| 1461 |
(template->pw_gecos = strdup(src->pw_gecos)) == NULL) |
| 1462 |
goto enomem; |
| 1463 |
if ((src->pw_fields & _PWF_DIR) && |
| 1464 |
(template->pw_dir = strdup(src->pw_dir)) == NULL) |
| 1465 |
goto enomem; |
| 1466 |
if ((src->pw_fields & _PWF_SHELL) && |
| 1467 |
(template->pw_shell = strdup(src->pw_shell)) == NULL) |
| 1468 |
goto enomem; |
| 1469 |
template->pw_fields = src->pw_fields; |
| 1470 |
return (0); |
| 1471 |
enomem: |
| 1472 |
syslog(LOG_ERR, "getpwent memory allocation failure"); |
| 1473 |
return (-1); |
| 1474 |
} |
| 1475 |
|
| 1476 |
|
| 1477 |
static int |
| 1478 |
compat_use_template(struct passwd *pwd, struct passwd *template, char *buffer, |
| 1479 |
size_t bufsize) |
| 1480 |
{ |
| 1481 |
struct passwd hold; |
| 1482 |
char *copy, *p, *q, *eob; |
| 1483 |
size_t n; |
| 1484 |
|
| 1485 |
/* We cannot know the layout of the password fields in `buffer', |
| 1486 |
* so we have to copy everything. |
| 1487 |
*/ |
| 1488 |
if (template->pw_fields == 0) /* nothing to fill-in */ |
| 1489 |
return (0); |
| 1490 |
n = 0; |
| 1491 |
n += pwd->pw_name != NULL ? strlen(pwd->pw_name) + 1 : 0; |
| 1492 |
n += pwd->pw_passwd != NULL ? strlen(pwd->pw_passwd) + 1 : 0; |
| 1493 |
n += pwd->pw_class != NULL ? strlen(pwd->pw_class) + 1 : 0; |
| 1494 |
n += pwd->pw_gecos != NULL ? strlen(pwd->pw_gecos) + 1 : 0; |
| 1495 |
n += pwd->pw_dir != NULL ? strlen(pwd->pw_dir) + 1 : 0; |
| 1496 |
n += pwd->pw_shell != NULL ? strlen(pwd->pw_shell) + 1 : 0; |
| 1497 |
copy = malloc(n); |
| 1498 |
if (copy == NULL) { |
| 1499 |
syslog(LOG_ERR, "getpwent memory allocation failure"); |
| 1500 |
return (ENOMEM); |
| 1501 |
} |
| 1502 |
p = copy; |
| 1503 |
eob = ©[n]; |
| 1504 |
#define COPY(field) do { \ |
| 1505 |
if (pwd->field == NULL) \ |
| 1506 |
hold.field = NULL; \ |
| 1507 |
else { \ |
| 1508 |
hold.field = p; \ |
| 1509 |
p += strlcpy(p, pwd->field, eob-p) + 1; \ |
| 1510 |
} \ |
| 1511 |
} while (0) |
| 1512 |
COPY(pw_name); |
| 1513 |
COPY(pw_passwd); |
| 1514 |
COPY(pw_class); |
| 1515 |
COPY(pw_gecos); |
| 1516 |
COPY(pw_dir); |
| 1517 |
COPY(pw_shell); |
| 1518 |
#undef COPY |
| 1519 |
p = buffer; |
| 1520 |
eob = &buffer[bufsize]; |
| 1521 |
#define COPY(field, flag) do { \ |
| 1522 |
q = (template->pw_fields & flag) ? template->field : hold.field; \ |
| 1523 |
if (q == NULL) \ |
| 1524 |
pwd->field = NULL; \ |
| 1525 |
else { \ |
| 1526 |
pwd->field = p; \ |
| 1527 |
if ((n = strlcpy(p, q, eob-p)) >= eob-p) { \ |
| 1528 |
free(copy); \ |
| 1529 |
return (ERANGE); \ |
| 1530 |
} \ |
| 1531 |
p += n + 1; \ |
| 1532 |
} \ |
| 1533 |
} while (0) |
| 1534 |
COPY(pw_name, 0); |
| 1535 |
#ifdef PW_OVERRIDE_PASSWD |
| 1536 |
COPY(pw_passwd, _PWF_PASSWD); |
| 1537 |
#else |
| 1538 |
COPY(pw_passwd, 0); |
| 1539 |
#endif |
| 1540 |
COPY(pw_class, 0); |
| 1541 |
COPY(pw_gecos, _PWF_GECOS); |
| 1542 |
COPY(pw_dir, _PWF_DIR); |
| 1543 |
COPY(pw_shell, _PWF_SHELL); |
| 1544 |
#undef COPY |
| 1545 |
#define COPY(field, flag) do { \ |
| 1546 |
if (template->pw_fields & flag) \ |
| 1547 |
pwd->field = template->field; \ |
| 1548 |
} while (0) |
| 1549 |
COPY(pw_uid, _PWF_UID); |
| 1550 |
COPY(pw_gid, _PWF_GID); |
| 1551 |
#undef COPY |
| 1552 |
free(copy); |
| 1553 |
return (0); |
| 1554 |
} |
| 1555 |
|
| 1556 |
|
| 1557 |
static int |
| 1558 |
compat_exclude(const char *name, DB **db) |
| 1559 |
{ |
| 1560 |
DBT key, data; |
| 1561 |
|
| 1562 |
if (*db == NULL && |
| 1563 |
(*db = dbopen(NULL, O_RDWR, 600, DB_HASH, 0)) == NULL) |
| 1564 |
return (errno); |
| 1565 |
key.size = strlen(name); |
| 1566 |
key.data = (char *)name; |
| 1567 |
data.size = 0; |
| 1568 |
data.data = NULL; |
| 1569 |
|
| 1570 |
if ((*db)->put(*db, &key, &data, 0) == -1) |
| 1571 |
return (errno); |
| 1572 |
return (0); |
| 1573 |
} |
| 1574 |
|
| 1575 |
|
| 1576 |
static int |
| 1577 |
compat_is_excluded(const char *name, DB *db) |
| 1578 |
{ |
| 1579 |
DBT key, data; |
| 1580 |
|
| 1581 |
if (db == NULL) |
| 1582 |
return (0); |
| 1583 |
key.size = strlen(name); |
| 1584 |
key.data = (char *)name; |
| 1585 |
return (db->get(db, &key, &data, 0) == 0); |
| 1586 |
} |
| 1587 |
|
| 1588 |
|
| 1589 |
static int |
| 1590 |
compat_redispatch(struct compat_state *st, enum nss_lookup_type how, |
| 1591 |
enum nss_lookup_type lookup_how, const char *name, const char *lookup_name, |
| 1592 |
uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, int *errnop) |
| 1593 |
{ |
| 1594 |
static const ns_src compatsrc[] = { |
| 1595 |
#ifdef YP |
| 1596 |
{ NSSRC_NIS, NS_SUCCESS }, |
| 1597 |
#endif |
| 1598 |
{ NULL, 0 } |
| 1599 |
}; |
| 1600 |
ns_dtab dtab[] = { |
| 1601 |
#ifdef YP |
| 1602 |
{ NSSRC_NIS, nis_passwd, NULL }, |
| 1603 |
#endif |
| 1604 |
#ifdef HESIOD |
| 1605 |
{ NSSRC_DNS, dns_passwd, NULL }, |
| 1606 |
#endif |
| 1607 |
{ NULL, NULL, NULL } |
| 1608 |
}; |
| 1609 |
void *discard; |
| 1610 |
int e, i, rv; |
| 1611 |
|
| 1612 |
for (i = 0; i < (int)(nitems(dtab) - 1); i++) |
| 1613 |
dtab[i].mdata = (void *)lookup_how; |
| 1614 |
more: |
| 1615 |
pwd_init(pwd); |
| 1616 |
switch (lookup_how) { |
| 1617 |
case nss_lt_all: |
| 1618 |
rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, |
| 1619 |
"getpwent_r", compatsrc, pwd, buffer, bufsize, |
| 1620 |
errnop); |
| 1621 |
break; |
| 1622 |
case nss_lt_id: |
| 1623 |
rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, |
| 1624 |
"getpwuid_r", compatsrc, uid, pwd, buffer, |
| 1625 |
bufsize, errnop); |
| 1626 |
break; |
| 1627 |
case nss_lt_name: |
| 1628 |
rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, |
| 1629 |
"getpwnam_r", compatsrc, lookup_name, pwd, buffer, |
| 1630 |
bufsize, errnop); |
| 1631 |
break; |
| 1632 |
default: |
| 1633 |
return (NS_UNAVAIL); |
| 1634 |
} |
| 1635 |
if (rv != NS_SUCCESS) |
| 1636 |
return (rv); |
| 1637 |
if (compat_is_excluded(pwd->pw_name, st->exclude)) { |
| 1638 |
if (how == nss_lt_all) |
| 1639 |
goto more; |
| 1640 |
return (NS_NOTFOUND); |
| 1641 |
} |
| 1642 |
e = compat_use_template(pwd, &st->template, buffer, bufsize); |
| 1643 |
if (e != 0) { |
| 1644 |
*errnop = e; |
| 1645 |
if (e == ERANGE) |
| 1646 |
return (NS_RETURN); |
| 1647 |
else |
| 1648 |
return (NS_UNAVAIL); |
| 1649 |
} |
| 1650 |
switch (how) { |
| 1651 |
case nss_lt_name: |
| 1652 |
if (strcmp(name, pwd->pw_name) != 0) |
| 1653 |
return (NS_NOTFOUND); |
| 1654 |
break; |
| 1655 |
case nss_lt_id: |
| 1656 |
if (uid != pwd->pw_uid) |
| 1657 |
return (NS_NOTFOUND); |
| 1658 |
break; |
| 1659 |
default: |
| 1660 |
break; |
| 1661 |
} |
| 1662 |
return (NS_SUCCESS); |
| 1663 |
} |
| 1664 |
|
| 1665 |
|
| 1666 |
static void |
| 1667 |
compat_endstate(void *p) |
| 1668 |
{ |
| 1669 |
struct compat_state *st; |
| 1670 |
|
| 1671 |
if (p == NULL) |
| 1672 |
return; |
| 1673 |
st = (struct compat_state *)p; |
| 1674 |
if (st->db != NULL) |
| 1675 |
st->db->close(st->db); |
| 1676 |
if (st->exclude != NULL) |
| 1677 |
st->exclude->close(st->exclude); |
| 1678 |
compat_clear_template(&st->template); |
| 1679 |
free(p); |
| 1680 |
} |
| 1681 |
|
| 1682 |
|
| 1683 |
static int |
| 1684 |
compat_setpwent(void *retval, void *mdata, va_list ap) |
| 1685 |
{ |
| 1686 |
static const ns_src compatsrc[] = { |
| 1687 |
#ifdef YP |
| 1688 |
{ NSSRC_NIS, NS_SUCCESS }, |
| 1689 |
#endif |
| 1690 |
{ NULL, 0 } |
| 1691 |
}; |
| 1692 |
ns_dtab dtab[] = { |
| 1693 |
#ifdef YP |
| 1694 |
{ NSSRC_NIS, nis_setpwent, NULL }, |
| 1695 |
#endif |
| 1696 |
#ifdef HESIOD |
| 1697 |
{ NSSRC_DNS, dns_setpwent, NULL }, |
| 1698 |
#endif |
| 1699 |
{ NULL, NULL, NULL } |
| 1700 |
}; |
| 1701 |
struct compat_state *st; |
| 1702 |
int rv, stayopen; |
| 1703 |
|
| 1704 |
#define set_setent(x, y) do { \ |
| 1705 |
int i; \ |
| 1706 |
for (i = 0; i < (int)(nitems(x) - 1); i++) \ |
| 1707 |
x[i].mdata = (void *)y; \ |
| 1708 |
} while (0) |
| 1709 |
|
| 1710 |
rv = compat_getstate(&st); |
| 1711 |
if (rv != 0) |
| 1712 |
return (NS_UNAVAIL); |
| 1713 |
switch ((enum constants)mdata) { |
| 1714 |
case SETPWENT: |
| 1715 |
stayopen = va_arg(ap, int); |
| 1716 |
st->keynum = 0; |
| 1717 |
if (stayopen) |
| 1718 |
st->db = pwdbopen(&st->version); |
| 1719 |
st->stayopen = stayopen; |
| 1720 |
set_setent(dtab, mdata); |
| 1721 |
(void)_nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "setpwent", |
| 1722 |
compatsrc, 0); |
| 1723 |
break; |
| 1724 |
case ENDPWENT: |
| 1725 |
if (st->db != NULL) { |
| 1726 |
(void)st->db->close(st->db); |
| 1727 |
st->db = NULL; |
| 1728 |
} |
| 1729 |
set_setent(dtab, mdata); |
| 1730 |
(void)_nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "endpwent", |
| 1731 |
compatsrc, 0); |
| 1732 |
break; |
| 1733 |
default: |
| 1734 |
break; |
| 1735 |
} |
| 1736 |
return (NS_UNAVAIL); |
| 1737 |
#undef set_setent |
| 1738 |
} |
| 1739 |
|
| 1740 |
|
| 1741 |
static int |
| 1742 |
compat_passwd(void *retval, void *mdata, va_list ap) |
| 1743 |
{ |
| 1744 |
char keybuf[MAXLOGNAME + 1]; |
| 1745 |
DBT key, entry; |
| 1746 |
struct compat_state *st; |
| 1747 |
enum nss_lookup_type how; |
| 1748 |
const char *name; |
| 1749 |
struct passwd *pwd; |
| 1750 |
char *buffer, *pw_name; |
| 1751 |
char *host, *user, *domain; |
| 1752 |
size_t bufsize; |
| 1753 |
uid_t uid; |
| 1754 |
uint32_t store; |
| 1755 |
int rv, from_compat, stayopen, *errnop; |
| 1756 |
|
| 1757 |
from_compat = 0; |
| 1758 |
name = NULL; |
| 1759 |
uid = (uid_t)-1; |
| 1760 |
how = (enum nss_lookup_type)mdata; |
| 1761 |
switch (how) { |
| 1762 |
case nss_lt_name: |
| 1763 |
name = va_arg(ap, const char *); |
| 1764 |
break; |
| 1765 |
case nss_lt_id: |
| 1766 |
uid = va_arg(ap, uid_t); |
| 1767 |
break; |
| 1768 |
case nss_lt_all: |
| 1769 |
break; |
| 1770 |
default: |
| 1771 |
rv = NS_NOTFOUND; |
| 1772 |
goto fin; |
| 1773 |
} |
| 1774 |
pwd = va_arg(ap, struct passwd *); |
| 1775 |
buffer = va_arg(ap, char *); |
| 1776 |
bufsize = va_arg(ap, size_t); |
| 1777 |
errnop = va_arg(ap, int *); |
| 1778 |
*errnop = compat_getstate(&st); |
| 1779 |
if (*errnop != 0) |
| 1780 |
return (NS_UNAVAIL); |
| 1781 |
if (how == nss_lt_all && st->keynum < 0) { |
| 1782 |
rv = NS_NOTFOUND; |
| 1783 |
goto fin; |
| 1784 |
} |
| 1785 |
if (st->db == NULL && |
| 1786 |
(st->db = pwdbopen(&st->version)) == NULL) { |
| 1787 |
*errnop = errno; |
| 1788 |
rv = NS_UNAVAIL; |
| 1789 |
goto fin; |
| 1790 |
} |
| 1791 |
if (how == nss_lt_all) { |
| 1792 |
if (st->keynum < 0) { |
| 1793 |
rv = NS_NOTFOUND; |
| 1794 |
goto fin; |
| 1795 |
} |
| 1796 |
stayopen = 1; |
| 1797 |
} else { |
| 1798 |
st->keynum = 0; |
| 1799 |
stayopen = st->stayopen; |
| 1800 |
} |
| 1801 |
docompat: |
| 1802 |
rv = NS_NOTFOUND; |
| 1803 |
switch (st->compat) { |
| 1804 |
case COMPAT_MODE_ALL: |
| 1805 |
rv = compat_redispatch(st, how, how, name, name, uid, pwd, |
| 1806 |
buffer, bufsize, errnop); |
| 1807 |
if (rv != NS_SUCCESS) |
| 1808 |
st->compat = COMPAT_MODE_OFF; |
| 1809 |
break; |
| 1810 |
case COMPAT_MODE_NETGROUP: |
| 1811 |
/* XXX getnetgrent is not thread-safe. */ |
| 1812 |
do { |
| 1813 |
rv = getnetgrent(&host, &user, &domain); |
| 1814 |
if (rv == 0) { |
| 1815 |
endnetgrent(); |
| 1816 |
st->compat = COMPAT_MODE_OFF; |
| 1817 |
rv = NS_NOTFOUND; |
| 1818 |
continue; |
| 1819 |
} else if (user == NULL || user[0] == '\0') |
| 1820 |
continue; |
| 1821 |
rv = compat_redispatch(st, how, nss_lt_name, name, |
| 1822 |
user, uid, pwd, buffer, bufsize, errnop); |
| 1823 |
} while (st->compat == COMPAT_MODE_NETGROUP && |
| 1824 |
!(rv & NS_TERMINATE)); |
| 1825 |
break; |
| 1826 |
case COMPAT_MODE_NAME: |
| 1827 |
rv = compat_redispatch(st, how, nss_lt_name, name, st->name, |
| 1828 |
uid, pwd, buffer, bufsize, errnop); |
| 1829 |
free(st->name); |
| 1830 |
st->name = NULL; |
| 1831 |
st->compat = COMPAT_MODE_OFF; |
| 1832 |
break; |
| 1833 |
default: |
| 1834 |
break; |
| 1835 |
} |
| 1836 |
if (rv & NS_TERMINATE) { |
| 1837 |
from_compat = 1; |
| 1838 |
goto fin; |
| 1839 |
} |
| 1840 |
key.data = keybuf; |
| 1841 |
rv = NS_NOTFOUND; |
| 1842 |
while (st->keynum >= 0) { |
| 1843 |
st->keynum++; |
| 1844 |
if (st->version < _PWD_CURRENT_VERSION) { |
| 1845 |
memcpy(&keybuf[1], &st->keynum, sizeof(st->keynum)); |
| 1846 |
key.size = sizeof(st->keynum) + 1; |
| 1847 |
} else { |
| 1848 |
store = htonl(st->keynum); |
| 1849 |
memcpy(&keybuf[1], &store, sizeof(store)); |
| 1850 |
key.size = sizeof(store) + 1; |
| 1851 |
} |
| 1852 |
keybuf[0] = _PW_VERSIONED(_PW_KEYBYNUM, st->version); |
| 1853 |
rv = st->db->get(st->db, &key, &entry, 0); |
| 1854 |
if (rv < 0 || rv > 1) { /* should never return > 1 */ |
| 1855 |
*errnop = errno; |
| 1856 |
rv = NS_UNAVAIL; |
| 1857 |
goto fin; |
| 1858 |
} else if (rv == 1) { |
| 1859 |
st->keynum = -1; |
| 1860 |
rv = NS_NOTFOUND; |
| 1861 |
goto fin; |
| 1862 |
} |
| 1863 |
pw_name = (char *)entry.data; |
| 1864 |
switch (pw_name[0]) { |
| 1865 |
case '+': |
| 1866 |
switch (pw_name[1]) { |
| 1867 |
case '\0': |
| 1868 |
st->compat = COMPAT_MODE_ALL; |
| 1869 |
break; |
| 1870 |
case '@': |
| 1871 |
setnetgrent(&pw_name[2]); |
| 1872 |
st->compat = COMPAT_MODE_NETGROUP; |
| 1873 |
break; |
| 1874 |
default: |
| 1875 |
st->name = strdup(&pw_name[1]); |
| 1876 |
if (st->name == NULL) { |
| 1877 |
syslog(LOG_ERR, |
| 1878 |
"getpwent memory allocation failure"); |
| 1879 |
*errnop = ENOMEM; |
| 1880 |
rv = NS_UNAVAIL; |
| 1881 |
break; |
| 1882 |
} |
| 1883 |
st->compat = COMPAT_MODE_NAME; |
| 1884 |
} |
| 1885 |
if (entry.size > bufsize) { |
| 1886 |
*errnop = ERANGE; |
| 1887 |
rv = NS_RETURN; |
| 1888 |
goto fin; |
| 1889 |
} |
| 1890 |
memcpy(buffer, entry.data, entry.size); |
| 1891 |
rv = pwdb_versions[st->version].parse(buffer, |
| 1892 |
entry.size, pwd, errnop); |
| 1893 |
if (rv != NS_SUCCESS) |
| 1894 |
; |
| 1895 |
else if (compat_set_template(pwd, &st->template) < 0) { |
| 1896 |
*errnop = ENOMEM; |
| 1897 |
rv = NS_UNAVAIL; |
| 1898 |
goto fin; |
| 1899 |
} |
| 1900 |
goto docompat; |
| 1901 |
case '-': |
| 1902 |
switch (pw_name[1]) { |
| 1903 |
case '\0': |
| 1904 |
/* XXX Maybe syslog warning */ |
| 1905 |
continue; |
| 1906 |
case '@': |
| 1907 |
setnetgrent(&pw_name[2]); |
| 1908 |
while (getnetgrent(&host, &user, &domain) != |
| 1909 |
0) { |
| 1910 |
if (user != NULL && user[0] != '\0') |
| 1911 |
compat_exclude(user, |
| 1912 |
&st->exclude); |
| 1913 |
} |
| 1914 |
endnetgrent(); |
| 1915 |
continue; |
| 1916 |
default: |
| 1917 |
compat_exclude(&pw_name[1], &st->exclude); |
| 1918 |
continue; |
| 1919 |
} |
| 1920 |
break; |
| 1921 |
default: |
| 1922 |
break; |
| 1923 |
} |
| 1924 |
if (compat_is_excluded((char *)entry.data, st->exclude)) |
| 1925 |
continue; |
| 1926 |
rv = pwdb_versions[st->version].match(entry.data, entry.size, |
| 1927 |
how, name, uid); |
| 1928 |
if (rv == NS_RETURN) |
| 1929 |
break; |
| 1930 |
else if (rv != NS_SUCCESS) |
| 1931 |
continue; |
| 1932 |
if (entry.size > bufsize) { |
| 1933 |
*errnop = ERANGE; |
| 1934 |
rv = NS_RETURN; |
| 1935 |
break; |
| 1936 |
} |
| 1937 |
memcpy(buffer, entry.data, entry.size); |
| 1938 |
rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd, |
| 1939 |
errnop); |
| 1940 |
if (rv & NS_TERMINATE) |
| 1941 |
break; |
| 1942 |
} |
| 1943 |
fin: |
| 1944 |
if (st->db != NULL && !stayopen) { |
| 1945 |
(void)st->db->close(st->db); |
| 1946 |
st->db = NULL; |
| 1947 |
} |
| 1948 |
if (rv == NS_SUCCESS) { |
| 1949 |
if (!from_compat) { |
| 1950 |
pwd->pw_fields &= ~_PWF_SOURCE; |
| 1951 |
pwd->pw_fields |= _PWF_FILES; |
| 1952 |
} |
| 1953 |
if (retval != NULL) |
| 1954 |
*(struct passwd **)retval = pwd; |
| 1955 |
} |
| 1956 |
return (rv); |
| 1957 |
} |
| 1958 |
|
| 1959 |
|
| 1960 |
/* |
| 1961 |
* common passwd line matching and parsing |
| 1962 |
*/ |
| 1963 |
int |
| 1964 |
__pw_match_entry(const char *entry, size_t entrysize, enum nss_lookup_type how, |
| 1965 |
const char *name, uid_t uid) |
| 1966 |
{ |
| 1967 |
const char *p, *eom; |
| 1968 |
char *q; |
| 1969 |
size_t len; |
| 1970 |
unsigned long m; |
| 1971 |
|
| 1972 |
eom = entry + entrysize; |
| 1973 |
for (p = entry; p < eom; p++) |
| 1974 |
if (*p == ':') |
| 1975 |
break; |
| 1976 |
if (*p != ':') |
| 1977 |
return (NS_NOTFOUND); |
| 1978 |
if (how == nss_lt_all) |
| 1979 |
return (NS_SUCCESS); |
| 1980 |
if (how == nss_lt_name) { |
| 1981 |
len = strlen(name); |
| 1982 |
if (len == (p - entry) && memcmp(name, entry, len) == 0) |
| 1983 |
return (NS_SUCCESS); |
| 1984 |
else |
| 1985 |
return (NS_NOTFOUND); |
| 1986 |
} |
| 1987 |
for (p++; p < eom; p++) |
| 1988 |
if (*p == ':') |
| 1989 |
break; |
| 1990 |
if (*p != ':') |
| 1991 |
return (NS_NOTFOUND); |
| 1992 |
m = strtoul(++p, &q, 10); |
| 1993 |
if (q[0] != ':' || (uid_t)m != uid) |
| 1994 |
return (NS_NOTFOUND); |
| 1995 |
else |
| 1996 |
return (NS_SUCCESS); |
| 1997 |
} |
| 1998 |
|
| 1999 |
|
| 2000 |
/* XXX buffer must be NUL-terminated. errnop is not set correctly. */ |
| 2001 |
int |
| 2002 |
__pw_parse_entry(char *buffer, size_t bufsize __unused, struct passwd *pwd, |
| 2003 |
int master, int *errnop __unused) |
| 2004 |
{ |
| 2005 |
|
| 2006 |
if (__pw_scan(buffer, pwd, master ? _PWSCAN_MASTER : 0) == 0) |
| 2007 |
return (NS_NOTFOUND); |
| 2008 |
else |
| 2009 |
return (NS_SUCCESS); |
| 2010 |
} |