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