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