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