4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
43 #include <sys/types.h>
46 #include <sys/resource.h>
48 #include <rpc/pmap_clnt.h>
49 #include <rpcsvc/yppasswd.h>
50 #include <netconfig.h>
58 /* must match sizes in passwd */
61 #define DEFDIR "/etc/"
62 #define MYPASSWD "passwd"
63 #define MYSHADOW "shadow"
64 #define DEFAULT_YPPASSWDD "/etc/default/yppasswdd"
65 #define YPPASSWDD_STR "check_restricted_shell_name=1"
67 /* The guts are in there */
68 extern void changepasswd(SVCXPRT
*);
70 static void boilerplate(struct svc_req
*rqstp
, SVCXPRT
*transp
);
71 static void unlimit(int lim
);
72 bool_t
validloginshell(char *sh
, char *arg
, int);
73 int validstr(char *str
, size_t size
);
75 extern char *getusershell(void);
76 extern void setusershell(void);
77 extern void endusershell(void);
81 int mflag
; /* do a make */
90 static char *defshell
= "/bin/sh";
92 /* These are the various reasons we might exit. */
113 static char err_usage
[] =
115 " rpc.yppasswdd [-D directory | passwd [passwd.adjunct]]\n"
116 " [-nopw] [-nogecos]\n"
117 " [-noshell] [-m arg1 arg2 ...]\n"
119 " directory is the directory where the passwd, shadow and/or\n"
120 " passwd.adjunct files are found (/etc by default)\n"
121 " It should match the setting of PWDIR in /var/yp/Makefile\n\n"
122 " Alternatively, the old 4.1.x syntax is supported where\n"
123 " passwd is the path to the passwd file\n"
124 " passwd.adjunct is the patch to the passwd.adjunct file\n"
126 " 1. The -D option and the passwd/passwd.adjunct arguments are\n"
127 " mutually exclusive\n"
128 " 2. The old syntax deprecated and will be removed in a future\n"
130 " 3. A shadow file found in the same directory as the passwd\n"
131 " will be assumed to contain the password information\n\n"
132 " arguments after -m are passed to make(1S) after password changes\n"
133 " -nopw passwords may not be changed remotely using passwd\n"
134 " -nogecos full name may not be changed remotely using passwd or chfn\n"
135 " -noshell shell may not be changed remotely using passwd or chsh\n";
137 char passwd_file
[FILENAME_MAX
], shadow_file
[FILENAME_MAX
];
138 char lockfile
[FILENAME_MAX
], adjunct_file
[FILENAME_MAX
];
141 main(int argc
, char **argv
)
143 SVCXPRT
*transp4
, *transp6
, *transpl
;
144 struct netconfig
*nconf4
, *nconf6
, *nconfl
;
145 int i
, tli4
, tli6
, stat
;
147 int dfexcl
; /* -D or files, not both flag */
148 enum exitstat exitstatus
= Esuccess
;
149 int connmaxrec
= RPC_MAXDATASIZE
;
151 strcpy(passwd_file
, DEFDIR MYPASSWD
);
152 strcpy(shadow_file
, DEFDIR MYSHADOW
);
153 strcpy(lockfile
, DEFDIR
".pwd.lock");
154 strcpy(adjunct_file
, DEFDIR
"security/passwd.adjunct");
159 for (i
= 1, errorflag
= 0, dfexcl
= 0; i
< argc
; i
++) {
160 if (argv
[i
][0] == '-' && argv
[i
][1] == 'm') {
161 if (access("/usr/ccs/bin/make", X_OK
) < 0)
163 "%s: /usr/ccs/bin/make is not available, "
164 "ignoring -m option",
171 } else if (argv
[i
][0] == '-' && argv
[i
][1] == 'D') {
175 strcpy(passwd_file
, argv
[i
]);
176 strcpy(shadow_file
, argv
[i
]);
177 strcpy(adjunct_file
, argv
[i
]);
178 strcpy(lockfile
, argv
[i
]);
179 if (argv
[i
][strlen(argv
[i
]) - 1] == '/') {
180 strcat(passwd_file
, MYPASSWD
);
181 strcat(shadow_file
, MYSHADOW
);
182 strcat(lockfile
, ".pwd.lock");
183 strcat(adjunct_file
, "security/passwd.adjunct");
185 strcat(passwd_file
, "/" MYPASSWD
);
186 strcat(shadow_file
, "/" MYSHADOW
);
187 strcat(lockfile
, "/.pwd.lock");
189 "/security/passwd.adjunct");
194 "rpc.yppasswdd: -D option requires a "
195 "directory argument\n");
197 exitstatus
= Emissingdir
;
202 "rpc.yppasswdd: cannot specify passwd/"
203 "passwd.adjunct pathnames AND use -D\n");
206 exitstatus
= EminusDandfiles
;
211 /* -single: Allow user to change only one of password, */
212 /* shell, or full name at a time. (WHY?) */
213 /* else if (strcmp(argv[i], "-single") == 0) */
215 /* else if (strcmp(argv[i], "-nosingle") == 0) */
217 } else if (strcmp(argv
[i
], "-nogecos") == 0)
219 else if (strcmp(argv
[i
], "-nopw") == 0)
221 else if (strcmp(argv
[i
], "-noshell") == 0)
223 else if (argv
[i
][0] != '-') {
225 * If we find a shadow file, we warn that we're
226 * using it in addition to warning that the user
227 * it using a deprecated syntax.
232 strcpy(passwd_file
, argv
[i
]);
233 memset(shadow_file
, 0, sizeof (shadow_file
));
234 strncpy(shadow_file
, argv
[i
],
235 strrchr(argv
[i
], '/') - argv
[i
] + 1);
236 strcat(shadow_file
, MYSHADOW
);
238 "rpc.yppasswdd: specifying the password file"
239 " on the command line is \n"
241 "consider using the -D option instead.\n");
242 if (access(shadow_file
, F_OK
) == 0) {
244 "rpc.yppasswdd: found a shadow file in "
245 "the same directory as %s\n"
246 " It will be used.\n",
249 if (i
+ 1 < argc
&& argv
[i
+1][0] != '-') {
250 strcpy(adjunct_file
, argv
[++i
]);
251 if (access(adjunct_file
, F_OK
) != 0) {
253 "rpc.yppasswdd: adjunct file %s "
256 exitstatus
= Emissingadjunct
;
263 "rpc.yppasswdd: cannot specify passwd/"
264 "passwd.adjunct pathnames AND use -D\n");
266 exitstatus
= EminusDandfiles
;
274 "rpc.yppasswdd: unrecognized option %s ignored\n",
280 fprintf(stderr
, err_usage
);
285 if (access(passwd_file
, W_OK
) < 0) {
286 fprintf(stderr
, "rpc.yppasswdd: can't access %s\n",
288 exitstatus
= Eaccesspasswd
;
290 if (access(shadow_file
, W_OK
) == 0) {
293 /* We don't demand a shadow file unless we're looking at /etc */
294 if (strcmp(DEFDIR MYSHADOW
, shadow_file
) == 0) {
295 fprintf(stderr
, "rpc.yppasswdd: can't access %s\n",
297 exitstatus
= Eaccessshadow
;
300 if (access(adjunct_file
, W_OK
) == 0) {
301 /* using an adjunct file */
305 if (chdir("/var/yp") < 0) {
306 fprintf(stderr
, "rpc.yppasswdd: can't chdir to /var/yp\n");
314 fprintf(stderr
, "\nProceeding.\n");
318 * Initialize locking system.
319 * This is required for N2L version which accesses the DBM files.
320 * For the non N2L version this sets up some locking which, since non
321 * N2L mode does not access the DBM files, will be unused.
323 * This also sets up yptol_mode.
325 if (!init_lock_system(TRUE
)) {
327 "rpc.yppasswdd: Cant initialize locking system\n");
332 /* Close everything, but stdin/stdout/stderr */
337 stat
= parseConfig(NULL
, NTOL_MAP_FILE
);
339 fprintf(stderr
, "yppasswdd : NIS to LDAP mapping"
341 } else if (stat
!= 0) {
342 fprintf(stderr
, "yppasswdd : Aborting after NIS to LDAP"
343 " mapping error.\n");
349 /* Wack umask that we inherited from parent */
352 /* Be a midwife to ourselves */
356 /* Disassociation is hard to do, la la la */
361 signal(SIGHUP
, SIG_IGN
);
362 signal(SIGINT
, SIG_IGN
);
363 signal(SIGWINCH
, SIG_IGN
);
364 signal(SIGTSTP
, SIG_IGN
);
365 signal(SIGTTIN
, SIG_IGN
);
366 signal(SIGTTOU
, SIG_IGN
);
367 signal(SIGCHLD
, SIG_IGN
);
370 * Just in case that wasn't enough, let's fork
371 * again. (per Stevens).
377 * We need stdin, stdout, and stderr later when we
380 freopen("/dev/null", "r+", stdin
);
381 freopen("/dev/null", "r+", stdout
);
382 freopen("/dev/null", "r+", stderr
);
385 openlog("yppasswdd", LOG_CONS
| LOG_PID
, LOG_AUTH
);
387 unlimit(RLIMIT_FSIZE
);
390 * Set non-blocking mode and maximum record size for
391 * connection oriented RPC transports.
393 if (!rpc_control(RPC_SVC_CONNMAXREC_SET
, &connmaxrec
)) {
394 syslog(LOG_INFO
, "unable to set maximum RPC record size");
397 nconf4
= getnetconfigent("udp");
398 nconf6
= getnetconfigent("udp6");
399 if (nconf4
== 0 && nconf6
== 0) {
400 syslog(LOG_ERR
, "udp/udp6 transport not supported\n");
401 exit(Egetnetconfigent
);
404 tli4
= (nconf4
!= 0) ? t_open(nconf4
->nc_device
, O_RDWR
, NULL
) : -1;
405 tli6
= (nconf6
!= 0) ? t_open(nconf6
->nc_device
, O_RDWR
, NULL
) : -1;
407 if (tli4
== -1 && tli6
== -1) {
408 syslog(LOG_ERR
, "can\'t open TLI endpoint(s)\n");
413 if (netdir_options(nconf4
, ND_SET_RESERVEDPORT
, tli4
, NULL
)) {
414 syslog(LOG_ERR
, "could not set reserved port: %s\n",
416 exit(Enetdir_rsvdport
);
420 if (netdir_options(nconf6
, ND_SET_RESERVEDPORT
, tli6
, NULL
)) {
421 syslog(LOG_ERR
, "could not set reserved port: %s\n",
423 exit(Enetdir_rsvdport
);
429 char *label
[2] = {"udp", "udp6"};
436 for (i
= 0; i
< sizeof (tli
)/sizeof (tli
[0]); i
++) {
437 fprintf(stderr
, "transport %s, fd = %d\n",
439 if ((state
= t_sync(tli
[i
])) < 0) {
440 fprintf(stderr
, "t_sync failed: %s\n",
444 if (t_getinfo(tli
[i
], &tinfo
) < 0) {
445 fprintf(stderr
, "t_getinfo failed: %s\n",
452 fprintf(stderr
, "TLI is unbound\n");
455 fprintf(stderr
, "TLI is idle\n");
459 "other side wants to release\n");
462 fprintf(stderr
, "T_INCON\n");
465 fprintf(stderr
, "T_DATAXFER\n");
468 fprintf(stderr
, "no state info, state = %d\n",
475 rpcb_unset((ulong_t
)YPPASSWDPROG
, (ulong_t
)YPPASSWDVERS
,
477 transp4
= svc_tli_create(tli4
, nconf4
, NULL
, 0, 0);
482 rpcb_unset((ulong_t
)YPPASSWDPROG
, (ulong_t
)YPPASSWDVERS
,
484 transp6
= svc_tli_create(tli6
, nconf6
, NULL
, 0, 0);
488 if (transp4
== 0 && transp6
== 0) {
489 syslog(LOG_ERR
, "yppasswdd: couldn't create an RPC server\n");
492 if (transp4
&& !svc_reg(transp4
, (ulong_t
)YPPASSWDPROG
,
493 (ulong_t
)YPPASSWDVERS
, boilerplate
, nconf4
)) {
494 syslog(LOG_ERR
, "yppasswdd: couldn't register yppasswdd\n");
497 if (transp6
&& !svc_reg(transp6
, (ulong_t
)YPPASSWDPROG
,
498 (ulong_t
)YPPASSWDVERS
, boilerplate
, nconf6
)) {
499 syslog(LOG_ERR
, "yppasswdd: couldn't register yppasswdd\n");
504 * Create a loopback RPC service for secure authentication of local
505 * principals -- we need this for accepting passwd updates from
506 * root on the master server.
508 if ((nconfl
= getnetconfigent("ticlts")) == NULL
) {
509 syslog(LOG_ERR
, "transport ticlts not supported\n");
510 exit(Egetnetconfigent
);
512 rpcb_unset((ulong_t
)YPPASSWDPROG
, (ulong_t
)YPPASSWDVERS
, nconfl
);
513 transpl
= svc_tli_create(RPC_ANYFD
, nconfl
, NULL
, 0, 0);
514 if (transpl
== NULL
) {
516 "yppasswdd: couldn't create an loopback RPC server\n");
519 if (!svc_reg(transpl
, (ulong_t
)YPPASSWDPROG
, (ulong_t
)YPPASSWDVERS
,
520 boilerplate
, nconfl
)) {
521 syslog(LOG_ERR
, "yppasswdd: couldn't register yppasswdd\n");
524 __rpc_negotiate_uid(transpl
->xp_fd
);
525 freenetconfigent(nconf4
);
526 freenetconfigent(nconf6
);
527 freenetconfigent(nconfl
);
529 syslog(LOG_ERR
, "yppasswdd: svc_run shouldn't have returned\n");
531 return (Esvcrun_ret
);
536 boilerplate(struct svc_req
*rqstp
, SVCXPRT
*transp
)
538 switch (rqstp
->rq_proc
) {
540 if (!svc_sendreply(transp
, xdr_void
, (char *)0))
542 "yppasswdd: couldn't reply to RPC call\n");
544 case YPPASSWDPROC_UPDATE
:
546 shim_changepasswd(transp
);
548 changepasswd(transp
);
554 validstr(char *str
, size_t size
)
558 if (str
== NULL
|| strlen(str
) > size
|| strchr(str
, ':'))
568 validloginshell(char *pw_shell
, char *arg
, int privileged
)
570 static char newshell
[STRSIZE
];
573 if (pw_shell
== 0 || *pw_shell
== '\0')
576 if ((defopen(DEFAULT_YPPASSWDD
)) == 0) {
577 if ((defread(YPPASSWDD_STR
)) != NULL
) {
578 cp
= strrchr(pw_shell
, '/');
586 "yppasswdd: cannot change "
587 "from restricted shell %s\n",
592 (void) defopen((char *)NULL
);
595 for (valid
= getusershell(); valid
; valid
= getusershell())
596 if (strcmp(pw_shell
, valid
) == 0)
599 if (valid
== NULL
&& !privileged
) {
600 syslog(LOG_ERR
, "yppasswdd: Current shell is not valid: %s\n",
607 strncpy(newshell
, arg
, sizeof (newshell
) - 1);
608 newshell
[sizeof (newshell
) - 1] = 0;
615 * Allow user to give shell name w/o preceding pathname.
618 for (valid
= getusershell(); valid
; valid
= getusershell()) {
619 if (newshell
[0] == '/') {
622 cp
= strrchr(valid
, '/');
628 if (strcmp(newshell
, cp
) == 0)
633 if (!privileged
|| newshell
[0] != '/') {
635 "%s is unacceptable as a new shell.\n",
643 if (access(valid
, X_OK
) < 0) {
644 syslog(LOG_WARNING
, "%s is unavailable.\n", valid
);
649 strncpy(newshell
, valid
, sizeof (newshell
));
659 rlim
.rlim_cur
= rlim
.rlim_max
= RLIM_INFINITY
;
660 setrlimit(lim
, &rlim
);