1 /* pwcheck.c -- Unix pwcheck daemon
2  */
3 /*
4  * Copyright (c) 1998-2016 Carnegie Mellon University.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  *
18  * 3. The name "Carnegie Mellon University" must not be used to
19  *    endorse or promote products derived from this software without
20  *    prior written permission. For permission or any other legal
21  *    details, please contact
22  *      Carnegie Mellon University
23  *      Center for Technology Transfer and Enterprise Creation
24  *      4615 Forbes Avenue
25  *      Suite 302
26  *      Pittsburgh, PA  15213
27  *      (412) 268-7393, fax: (412) 268-7395
28  *      innovation@andrew.cmu.edu
29  *
30  * 4. Redistributions of any form whatsoever must retain the following
31  *    acknowledgment:
32  *    "This product includes software developed by Computing Services
33  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
34  *
35  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
36  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
37  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
38  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
39  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
40  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
41  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
42  */
43 
44 #include <config.h>
45 
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif
49 #include <stdio.h>
50 #include <errno.h>
51 #include <sys/types.h>
52 #include <sys/param.h>
53 #include <sys/socket.h>
54 #include <sys/un.h>
55 #include <sys/stat.h>
56 #include <fcntl.h>
57 #ifdef HAVE_PATHS_H
58 #include <paths.h>
59 #endif
60 #include <syslog.h>
61 
62 #if !defined(_PATH_PWCHECKPID)
63 #ifdef _PATH_VARRUN
64 # define _PATH_PWCHECKPID (_PATH_VARRUN "pwcheck.pid")
65 #else
66 # define _PATH_PWCHECKPID (NULL)
67 #endif
68 #endif
69 
70 void newclient(int);
71 int retry_write(int, const char *, unsigned int);
72 
73 /*
74  * Unix pwcheck daemon-authenticated login (shadow password)
75  */
76 
77 int
main()78 main()
79 {
80     char fnamebuf[MAXPATHLEN];
81     int s;
82     int c;
83     int count;
84     int rc;
85     struct sockaddr_un srvaddr;
86     struct sockaddr_un clientaddr;
87     int r;
88     int len;
89     mode_t oldumask;
90     char *pid_file = _PATH_PWCHECKPID;
91     FILE *fp = NULL;
92     pid_t pid;
93 
94     openlog("pwcheck", LOG_NDELAY, LOG_AUTH);
95 
96     /* Daemonize. */
97     count = 5;
98     while (count--) {
99 	pid = fork();
100 
101 	if (pid > 0)
102 	    _exit(0);               /* parent dies */
103 
104 	if ((pid == -1) && (errno == EAGAIN)) {
105 	    syslog(LOG_WARNING, "master fork failed (sleeping): %m");
106 	    sleep(5);
107 	    continue;
108 	}
109     }
110     if (pid == -1) {
111 	rc = errno;
112 	syslog(LOG_ERR, "FATAL: master fork failed: %m");
113 	fprintf(stderr, "pwcheck: ");
114 	errno = rc;
115 	perror("fork");
116 	exit(1);
117     }
118 
119     /*
120      * We're now running in the child. Lose our controlling terminal
121      * and obtain a new process group.
122      */
123     if (setsid() == -1) {
124 	rc = errno;
125 	syslog(LOG_ERR, "FATAL: setsid: %m");
126 	fprintf(stderr, "pwcheck: ");
127 	errno = rc;
128 	perror("setsid");
129 	exit(1);
130     }
131 
132     s = open("/dev/null", O_RDWR, 0);
133     if (s == -1) {
134 	rc = errno;
135 	syslog(LOG_ERR, "FATAL: /dev/null: %m");
136 	fprintf(stderr, "pwcheck: ");
137 	errno = rc;
138 	perror("/dev/null");
139 	exit(1);
140 
141     }
142     dup2(s, fileno(stdin));
143     dup2(s, fileno(stdout));
144     dup2(s, fileno(stderr));
145     if (s > 2) {
146 	close(s);
147     }
148 
149     /*
150      *   Record process ID - shamelessly stolen from inetd (I.V.)
151      */
152     pid = getpid();
153     if (pid_file) {
154 	fp = fopen(pid_file, "w");
155     }
156     if (fp) {
157         fprintf(fp, "%ld\n", (long)pid);
158         fclose(fp);
159     } else if (pid_file) {
160         syslog(LOG_WARNING, "%s: %m", pid_file);
161     }
162 
163     s = socket(AF_UNIX, SOCK_STREAM, 0);
164     if (s == -1) {
165 	perror("socket");
166 	exit(1);
167     }
168 
169     strncpy(fnamebuf, PWCHECKDIR, sizeof(fnamebuf));
170     strncpy(fnamebuf + sizeof(PWCHECKDIR)-1, "/pwcheck",
171 	    sizeof(fnamebuf) - sizeof(PWCHECKDIR));
172     fnamebuf[MAXPATHLEN-1] = '\0';
173 
174     (void) unlink(fnamebuf);
175 
176     memset((char *)&srvaddr, 0, sizeof(srvaddr));
177     srvaddr.sun_family = AF_UNIX;
178     strncpy(srvaddr.sun_path, fnamebuf, sizeof(srvaddr.sun_path));
179     /* Most systems make sockets 0777 no matter what you ask for.
180        Known exceptions are Linux and DUX. */
181     oldumask = umask((mode_t) 0); /* for Linux, which observes the umask when
182 			    setting up the socket */
183     r = bind(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr));
184     if (r == -1) {
185 	syslog(LOG_ERR, "%.*s: %m",
186 	       sizeof(srvaddr.sun_path), srvaddr.sun_path);
187 	exit(1);
188     }
189     umask(oldumask); /* for Linux */
190     chmod(fnamebuf, (mode_t) 0777); /* for DUX, where this isn't the default.
191 				    (harmlessly fails on some systems) */
192     r = listen(s, 5);
193     if (r == -1) {
194 	syslog(LOG_ERR, "listen: %m");
195 	exit(1);
196     }
197 
198     for (;;) {
199 	len = sizeof(clientaddr);
200 	c = accept(s, (struct sockaddr *)&clientaddr, &len);
201 	if (c == -1 && errno != EINTR) {
202 	    syslog(LOG_WARNING, "accept: %m");
203 	    continue;
204 	}
205 
206 	newclient(c);
207     }
208 }
209 
newclient(int c)210 void newclient(int c)
211 {
212     char request[1024];
213     int n;
214     unsigned int start;
215     char *reply;
216     extern char *pwcheck();
217 
218     start = 0;
219     while (start < sizeof(request) - 1) {
220 	n = read(c, request+start, sizeof(request) - 1 - start);
221 	if (n < 1) {
222 	    reply = "Error reading request";
223 	    goto sendreply;
224 	}
225 
226 	start += n;
227 
228 	if (request[start-1] == '\0' && strlen(request) < start) {
229 	    break;
230 	}
231     }
232 
233     if (start >= sizeof(request) - 1) {
234 	reply = "Request too big";
235     }
236     else {
237 	reply = pwcheck(request, request + strlen(request) + 1);
238     }
239 
240 sendreply:
241 
242     retry_write(c, reply, strlen(reply));
243     close(c);
244 }
245 
246 /*
247  * Keep calling the write() system call with 'fd', 'buf', and 'nbyte'
248  * until all the data is written out or an error occurs.
249  */
retry_write(int fd,const char * buf,unsigned int nbyte)250 int retry_write(int fd, const char *buf, unsigned int nbyte)
251 {
252     int n;
253     int written = 0;
254 
255     if (nbyte == 0)
256 	return 0;
257 
258     for (;;) {
259         n = write(fd, buf, nbyte);
260         if (n == -1) {
261             if (errno == EINTR)
262 		continue;
263             return -1;
264         }
265 
266         written += n;
267 
268         if ((unsigned int) n >= nbyte)
269 	    return written;
270 
271         buf += n;
272         nbyte -= n;
273     }
274 }
275