1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2024 OmniOS Community Edition (OmniOSce) Association.
25 */
26
27 #include <stdio.h>
28 #include <errno.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/errno.h>
32 #include <pwd.h>
33 #include <unistd.h>
34 #include <syslog.h>
35
36 #include <netdb.h>
37
38 #include <rpc/rpc.h>
39 #include <rpcsvc/yppasswd.h>
40 #include <rpcsvc/ypclnt.h>
41 #include <rpcsvc/yp_prot.h>
42
43 #include "passwdutil.h"
44
45 int nis_getattr(const char *name, attrlist *item, pwu_repository_t *rep);
46 int nis_getpwnam(const char *name, attrlist *items, pwu_repository_t *rep,
47 void **buf);
48 int nis_update(attrlist *items, pwu_repository_t *rep, void *buf);
49 int nis_putpwnam(const char *name, const char *oldpw, pwu_repository_t *rep,
50 void *buf);
51 int nis_user_to_authenticate(const char *user, pwu_repository_t *rep,
52 char **auth_user, int *privileged);
53
54 /*
55 * nis function pointer table, used by passwdutil_init to initialize
56 * the global Repository-OPerations table "rops"
57 */
58 struct repops nis_repops = {
59 NULL, /* checkhistory */
60 nis_getattr,
61 nis_getpwnam,
62 nis_update,
63 nis_putpwnam,
64 nis_user_to_authenticate,
65 NULL, /* lock */
66 NULL /* unlock */
67 };
68
69 /*
70 * structure used to keep state between get/update/put calls
71 */
72 typedef struct {
73 char *domain;
74 char *master;
75 char *scratch;
76 int scratchlen;
77 char *c2scratch;
78 int c2scratchlen;
79 struct passwd *pwd;
80 } nisbuf_t;
81
82 /*
83 * Are we a 'privileged' process? Yes if we are running on the
84 * NIS server AND we are root...
85 */
86 int
nis_privileged(nisbuf_t * nisbuf)87 nis_privileged(nisbuf_t *nisbuf)
88 {
89 char thishost[MAXHOSTNAMELEN];
90 if (gethostname(thishost, sizeof (thishost)) == -1) {
91 syslog(LOG_ERR, "passwdutil.so: Can't get hostname");
92 return (0);
93 }
94
95 if (strcmp(nisbuf->master, thishost) != 0)
96 return (0);
97
98 /* We're running on the NIS server. */
99 return (getuid() == 0);
100 }
101
102 /*
103 * nis_to_pwd()
104 *
105 * convert password-entry-line to "struct passwd"
106 */
107 void
nis_to_pwd(char * nis,struct passwd * pwd)108 nis_to_pwd(char *nis, struct passwd *pwd)
109 {
110 pwd->pw_name = strsep(&nis, ":");
111 pwd->pw_passwd = strsep(&nis, ":");
112 pwd->pw_uid = atoi(strsep(&nis, ":"));
113 pwd->pw_gid = atoi(strsep(&nis, ":"));
114 pwd->pw_gecos = strsep(&nis, ":");
115 pwd->pw_dir = strsep(&nis, ":");
116 pwd->pw_shell = nis;
117 if (pwd->pw_shell[0])
118 pwd->pw_shell[strlen(pwd->pw_shell)-1] = '\0';
119 }
120
121 /*
122 * nis_user_to_authenticate(name, rep, auth_user, privileged)
123 *
124 */
125 /*ARGSUSED*/
126 int
nis_user_to_authenticate(const char * user,pwu_repository_t * rep,char ** auth_user,int * privileged)127 nis_user_to_authenticate(const char *user, pwu_repository_t *rep,
128 char **auth_user, int *privileged)
129 {
130 nisbuf_t *buf = NULL;
131 int res;
132 attrlist attr_tmp[1];
133 uid_t uid;
134
135 /*
136 * special NIS case: don't bother to get "root" from NIS
137 */
138 if (strcmp(user, "root") == 0)
139 return (PWU_NOT_FOUND);
140
141 attr_tmp[0].type = ATTR_UID;
142 attr_tmp[0].next = NULL;
143
144 res = nis_getpwnam(user, &attr_tmp[0], rep, (void **)&buf);
145
146 if (res != PWU_SUCCESS)
147 return (res);
148
149 if (nis_privileged(buf)) {
150 *privileged = 1;
151 *auth_user = NULL;
152 res = PWU_SUCCESS;
153 } else {
154 uid = getuid();
155
156 *privileged = (uid == (uid_t)0);
157
158 /* root, or user herself can change attributes */
159 if (uid == 0 || uid == buf->pwd->pw_uid) {
160 *auth_user = strdup(user);
161 res = PWU_SUCCESS;
162 } else {
163 res = PWU_DENIED;
164 }
165 }
166
167 /*
168 * Do not release buf->domain.
169 * It's been set by yp_get_default_domain()
170 * and must not be freed.
171 * See man page yp_get_default_domain(3NSL)
172 * for details.
173 */
174 if (buf->master)
175 free(buf->master);
176 if (buf->scratch)
177 free(buf->scratch);
178 if (buf->c2scratch)
179 free(buf->c2scratch);
180 free(buf->pwd);
181 free(buf);
182
183 return (res);
184 }
185
186
187 /*
188 * nis_getattr(name, items, rep)
189 *
190 * get account attributes specified in 'items'
191 */
192 int
nis_getattr(const char * name,attrlist * items,pwu_repository_t * rep)193 nis_getattr(const char *name, attrlist *items, pwu_repository_t *rep)
194 {
195 nisbuf_t *nisbuf = NULL;
196 struct passwd *pw;
197 attrlist *w;
198 int res;
199
200 res = nis_getpwnam(name, items, rep, (void **)&nisbuf);
201 if (res != PWU_SUCCESS)
202 return (res);
203
204 pw = nisbuf->pwd;
205
206 for (w = items; w != NULL; w = w->next) {
207 switch (w->type) {
208 case ATTR_NAME:
209 if ((w->data.val_s = strdup(pw->pw_name)) == NULL)
210 res = PWU_NOMEM;
211 break;
212 case ATTR_COMMENT:
213 if ((w->data.val_s = strdup(pw->pw_comment)) == NULL)
214 res = PWU_NOMEM;
215 break;
216 case ATTR_GECOS:
217 if ((w->data.val_s = strdup(pw->pw_gecos)) == NULL)
218 res = PWU_NOMEM;
219 break;
220 case ATTR_HOMEDIR:
221 if ((w->data.val_s = strdup(pw->pw_dir)) == NULL)
222 res = PWU_NOMEM;
223 break;
224 case ATTR_SHELL:
225 if ((w->data.val_s = strdup(pw->pw_shell)) == NULL)
226 res = PWU_NOMEM;
227 break;
228 case ATTR_PASSWD:
229 case ATTR_PASSWD_SERVER_POLICY:
230 if ((w->data.val_s = strdup(pw->pw_passwd)) == NULL)
231 res = PWU_NOMEM;
232 break;
233 case ATTR_REP_NAME:
234 if ((w->data.val_s = strdup("nis")) == NULL)
235 res = PWU_NOMEM;
236 break;
237
238 /* integer values */
239 case ATTR_UID:
240 w->data.val_i = nisbuf->pwd->pw_uid;
241 break;
242 case ATTR_GID:
243 w->data.val_i = nisbuf->pwd->pw_gid;
244 break;
245 case ATTR_LSTCHG:
246 case ATTR_MIN:
247 case ATTR_MAX:
248 case ATTR_WARN:
249 case ATTR_INACT:
250 case ATTR_EXPIRE:
251 case ATTR_FLAG:
252 case ATTR_AGE:
253 w->data.val_i = -1; /* not used for NIS */
254 break;
255 default:
256 break;
257 }
258 }
259
260 /*
261 * Do not release nisbuf->domain.
262 * It's been set by yp_get_default_domain()
263 * and must not be freed.
264 * See man page yp_get_default_domain(3NSL)
265 * for details.
266 */
267 if (nisbuf->master)
268 free(nisbuf->master);
269 if (nisbuf->scratch)
270 free(nisbuf->scratch);
271 if (nisbuf->c2scratch)
272 free(nisbuf->c2scratch);
273 free(nisbuf->pwd);
274 free(nisbuf);
275
276 return (res);
277 }
278
279 /*
280 * nis_getpwnam(name, items, rep)
281 *
282 * Get the account information of user 'name'
283 */
284 /*ARGSUSED*/
285 int
nis_getpwnam(const char * name,attrlist * items,pwu_repository_t * rep,void ** buf)286 nis_getpwnam(const char *name, attrlist *items, pwu_repository_t *rep,
287 void **buf)
288 {
289 nisbuf_t *nisbuf;
290 int nisresult;
291 char *ncname;
292
293 nisbuf = calloc(sizeof (*nisbuf), 1);
294 if (nisbuf == NULL)
295 return (PWU_NOMEM);
296
297 nisbuf->pwd = malloc(sizeof (struct passwd));
298 if (nisbuf->pwd == NULL) {
299 free(nisbuf);
300 return (PWU_NOMEM);
301 }
302
303 /*
304 * Do not release nisbuf->domain.
305 * It is going to be set by yp_get_default_domain()
306 * and must not be freed.
307 * See man page yp_get_default_domain(3NSL)
308 * for details.
309 */
310 if (yp_get_default_domain(&nisbuf->domain) != 0) {
311 syslog(LOG_ERR, "passwdutil.so: can't get domain");
312 free(nisbuf->pwd);
313 free(nisbuf);
314 return (PWU_SERVER_ERROR);
315 }
316
317 if (yp_master(nisbuf->domain, "passwd.byname", &nisbuf->master) != 0) {
318 syslog(LOG_ERR,
319 "passwdutil.so: can't get master for passwd map");
320 free(nisbuf->master);
321 free(nisbuf->pwd);
322 free(nisbuf);
323 return (PWU_SERVER_ERROR);
324 }
325
326 ncname = strdup(name);
327 if (ncname == NULL) {
328 free(nisbuf->master);
329 free(nisbuf->pwd);
330 free(nisbuf);
331 return (PWU_NOMEM);
332 }
333
334 nisresult = yp_match(nisbuf->domain, "passwd.byname", ncname,
335 strlen(ncname), &(nisbuf->scratch),
336 &(nisbuf->scratchlen));
337 free(ncname);
338 if (nisresult != 0) {
339 free(nisbuf->pwd);
340 free(nisbuf->scratch);
341 free(nisbuf->master);
342 free(nisbuf);
343 return (PWU_NOT_FOUND);
344 }
345
346 nis_to_pwd(nisbuf->scratch, nisbuf->pwd);
347
348 /*
349 * check for the C2 security flag "##" in the passwd field.
350 * If the first 2 chars in the passwd field is "##", get
351 * the user's passwd from passwd.adjunct.byname map.
352 * The lookup to this passwd.adjunct.byname map will only
353 * succeed if the caller's uid is 0 because only root user
354 * can use privilege port.
355 */
356 if (nisbuf->pwd->pw_passwd[0] == '#' &&
357 nisbuf->pwd->pw_passwd[1] == '#') {
358 char *key = &nisbuf->pwd->pw_passwd[2];
359 int keylen;
360 char *p;
361
362 keylen = strlen(key);
363
364 nisresult = yp_match(nisbuf->domain, "passwd.adjunct.byname",
365 key, keylen, &(nisbuf->c2scratch),
366 &(nisbuf->c2scratchlen));
367
368 if (nisresult == 0 && nisbuf->c2scratch != NULL) {
369 /* Skip username (first field), and pick up password */
370 p = nisbuf->c2scratch;
371 (void) strsep(&p, ":");
372 nisbuf->pwd->pw_passwd = strsep(&p, ":");
373 }
374 }
375
376 *buf = (void *)nisbuf;
377
378 return (PWU_SUCCESS);
379 }
380
381 /*
382 * nis_update(items, rep, buf)
383 *
384 * update the information in "buf" with the attribute/values
385 * specified in "items".
386 */
387 /*ARGSUSED*/
388 int
nis_update(attrlist * items,pwu_repository_t * rep,void * buf)389 nis_update(attrlist *items, pwu_repository_t *rep, void *buf)
390 {
391 attrlist *p;
392 nisbuf_t *nisbuf = (nisbuf_t *)buf;
393 char *salt;
394
395 for (p = items; p != NULL; p = p->next) {
396 switch (p->type) {
397 case ATTR_NAME:
398 break;
399 /*
400 * Nothing special needs to be done for
401 * server policy
402 */
403 case ATTR_PASSWD:
404 case ATTR_PASSWD_SERVER_POLICY:
405 salt = crypt_gensalt(
406 nisbuf->pwd->pw_passwd, nisbuf->pwd);
407
408 if (salt == NULL) {
409 if (errno == ENOMEM)
410 return (PWU_NOMEM);
411 else {
412 /* algorithm problem? */
413 syslog(LOG_AUTH | LOG_ALERT,
414 "passwdutil: crypt_gensalt "
415 "%m");
416 return (PWU_UPDATE_FAILED);
417 }
418 }
419 nisbuf->pwd->pw_passwd = crypt(p->data.val_s, salt);
420 free(salt);
421 break;
422 case ATTR_UID:
423 nisbuf->pwd->pw_uid = (uid_t)p->data.val_i;
424 break;
425 case ATTR_GID:
426 nisbuf->pwd->pw_gid = (gid_t)p->data.val_i;
427 break;
428 case ATTR_AGE:
429 nisbuf->pwd->pw_age = p->data.val_s;
430 break;
431 case ATTR_COMMENT:
432 nisbuf->pwd->pw_comment = p->data.val_s;
433 break;
434 case ATTR_GECOS:
435 nisbuf->pwd->pw_gecos = p->data.val_s;
436 break;
437 case ATTR_HOMEDIR:
438 nisbuf->pwd->pw_dir = p->data.val_s;
439 break;
440 case ATTR_SHELL:
441 nisbuf->pwd->pw_shell = p->data.val_s;
442 break;
443 case ATTR_LSTCHG:
444 case ATTR_MIN:
445 case ATTR_MAX:
446 case ATTR_WARN:
447 case ATTR_INACT:
448 case ATTR_EXPIRE:
449 case ATTR_FLAG:
450 default:
451 break;
452 }
453 }
454 return (PWU_SUCCESS);
455 }
456
457 /*
458 * nis_putpwnam(name, oldpw, rep, buf)
459 *
460 * Update the NIS server. The passwd structure in buf will be sent to
461 * the server for user "name" authenticating with password "oldpw".
462 */
463 /*ARGSUSED*/
464 int
nis_putpwnam(const char * name,const char * oldpw,pwu_repository_t * rep,void * buf)465 nis_putpwnam(const char *name, const char *oldpw, pwu_repository_t *rep,
466 void *buf)
467 {
468 nisbuf_t *nisbuf = (nisbuf_t *)buf;
469 struct yppasswd yppasswd;
470 struct netconfig *nconf;
471 int ok;
472 enum clnt_stat ans;
473 CLIENT *client;
474 struct timeval timeout;
475 char *oldpass;
476
477 if (strcmp(name, "root") == 0)
478 return (PWU_NOT_FOUND);
479
480 oldpass = yppasswd.oldpass = strdup(oldpw != NULL ? oldpw : "");
481 if (oldpass == NULL)
482 return (PWU_NOMEM);
483 yppasswd.newpw = *nisbuf->pwd;
484
485 /*
486 * If we are privileged, we create a ticlts connection to the
487 * NIS server so that it can check our credentials
488 */
489 if (nis_privileged(nisbuf)) {
490 nconf = getnetconfigent("ticlts");
491 if (!nconf) {
492 free(oldpass);
493 syslog(LOG_ERR,
494 "passwdutil.so: Couldn't get netconfig entry");
495 return (PWU_SYSTEM_ERROR);
496 }
497 client = clnt_tp_create(nisbuf->master, YPPASSWDPROG,
498 YPPASSWDVERS, nconf);
499 freenetconfigent(nconf);
500 } else {
501 /* Try IPv6 first */
502 client = clnt_create(nisbuf->master, YPPASSWDPROG,
503 YPPASSWDVERS, "udp6");
504 if (client == NULL)
505 client = clnt_create(nisbuf->master, YPPASSWDPROG,
506 YPPASSWDVERS, "udp");
507 }
508
509 if (client == NULL) {
510 free(oldpass);
511 syslog(LOG_ERR,
512 "passwdutil.so: couldn't create client to YP master");
513 return (PWU_SERVER_ERROR);
514 }
515
516 timeout.tv_usec = 0;
517 timeout.tv_sec = 55; /* ndp uses 55 seconds */
518
519 ans = CLNT_CALL(client, YPPASSWDPROC_UPDATE, xdr_yppasswd,
520 (char *)&yppasswd, xdr_int, (char *)&ok, timeout);
521
522 free(oldpass);
523 free(nisbuf->pwd);
524 free(nisbuf->master);
525 free(nisbuf->scratch);
526 free(nisbuf->c2scratch);
527
528 (void) clnt_destroy(client);
529
530 if (ans != RPC_SUCCESS) {
531 return (PWU_UPDATE_FAILED);
532 }
533
534 /* These errors are obtained from the yppasswdd.c code */
535 switch (ok) {
536 case 2: return (PWU_DENIED);
537 case 8: return (PWU_BUSY);
538 case 9: return (PWU_SERVER_ERROR);
539 case 4: return (PWU_NOT_FOUND);
540 case 3: return (PWU_NO_CHANGE);
541 case 7: return (PWU_DENIED);
542 case 0: return (PWU_SUCCESS);
543 default: return (PWU_SYSTEM_ERROR);
544 }
545 }
546