1 /* $OpenBSD: virtual.c,v 1.6 2004/06/20 05:18:07 itojun Exp $ */
2
3 /*
4 * Virtual domain support.
5 */
6
7 #include "params.h"
8
9 #if POP_VIRTUAL
10
11 #define _XOPEN_SOURCE 4
12 #define _XOPEN_SOURCE_EXTENDED
13 #define _XOPEN_VERSION 4
14 #define _XPG4_2
15 #include <stdio.h>
16 #include <unistd.h>
17 #include <fcntl.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <limits.h>
21 #include <errno.h>
22 #include <pwd.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28
29 #ifndef NAME_MAX
30 #define NAME_MAX 255
31 #endif
32
33 extern int log_error(char *s);
34
35 char *virtual_domain;
36 char *virtual_spool;
37
virtual_startup(void)38 int virtual_startup(void)
39 {
40 return 0;
41 }
42
lookup(void)43 static char *lookup(void)
44 {
45 struct sockaddr_storage ss;
46 int length;
47 int error;
48 static char hbuf[NI_MAXHOST];
49
50 length = sizeof(ss);
51 if (getsockname(0, (struct sockaddr *)&ss, &length)) {
52 if (errno == ENOTSOCK) return "";
53 log_error("getsockname");
54 return NULL;
55 }
56
57 error = getnameinfo((struct sockaddr *)&ss, length, hbuf, sizeof(hbuf),
58 NULL, 0, NI_NUMERICHOST);
59 if (error) {
60 /* logging? */
61 return NULL;
62 }
63
64 return hbuf;
65 }
66
is_valid_user(char * user)67 static int is_valid_user(char *user)
68 {
69 unsigned char *p;
70
71 /* This is pretty liberal, but we're going to use direct syscalls only,
72 * and they have to accept all the printable characters */
73 for (p = (unsigned char *)user; *p; p++)
74 if (*p < ' ' || *p > 0x7E || *p == '.' || *p == '/') return 0;
75
76 if (p - (unsigned char *)user > NAME_MAX) return 0;
77
78 return 1;
79 }
80
virtual_userpass(char * user,char * pass,int * known)81 struct passwd *virtual_userpass(char *user, char *pass, int *known)
82 {
83 struct passwd *pw, *result;
84 struct stat stat;
85 char auth[VIRTUAL_AUTH_SIZE];
86 char *address, *pathname;
87 char *template, *passwd;
88 int fail;
89 int fd, size;
90
91 *known = 0;
92
93 /* Make sure we don't try to authenticate globally if something fails
94 * before we find out whether the virtual domain is known to us */
95 virtual_domain = "UNKNOWN";
96 virtual_spool = NULL;
97
98 if (!(address = lookup())) return NULL;
99
100 /* Authenticate globally (if supported) if run on a non-socket */
101 if (!*address) {
102 virtual_domain = NULL;
103 return NULL;
104 }
105
106 fail = 0;
107 if (!is_valid_user(user)) {
108 user = "INVALID";
109 fail = 1;
110 }
111
112 /* This "can't happen", but is just too critical to not check explicitly */
113 if (strchr(address, '/') || strchr(user, '/'))
114 return NULL;
115
116 if (asprintf(&pathname, "%s/%s", VIRTUAL_HOME_PATH, address) == -1)
117 return NULL;
118
119 if (lstat(pathname, &stat)) {
120 if (errno == ENOENT)
121 virtual_domain = NULL;
122 else
123 log_error("lstat");
124 free(pathname);
125 return NULL;
126 }
127
128 if (!(address = strdup(address))) {
129 free(pathname);
130 return NULL;
131 }
132 virtual_domain = address;
133
134 free(pathname);
135
136 if (asprintf(&pathname, "%s/%s/%s/%s", VIRTUAL_HOME_PATH, address,
137 VIRTUAL_AUTH_PATH, user) == -1)
138 return NULL;
139
140 if ((fd = open(pathname, O_RDONLY)) < 0 && errno != ENOENT) {
141 log_error("open");
142 fail = 1;
143 }
144
145 free(pathname);
146
147 if (asprintf(&virtual_spool, "%s/%s/%s", VIRTUAL_HOME_PATH,
148 virtual_domain, VIRTUAL_SPOOL_PATH) == -1) {
149 close(fd);
150 return NULL;
151 }
152
153 size = 0;
154 if (fd >= 0) {
155 *known = !fail;
156
157 if ((size = read(fd, auth, sizeof(auth))) < 0) {
158 log_error("read");
159 size = 0;
160 fail = 1;
161 }
162
163 close(fd);
164 }
165
166 if (size >= sizeof(auth)) {
167 size = 0;
168 fail = 1;
169 }
170
171 auth[size] = 0;
172
173 if (!(template = strtok(auth, ":")) || !*template) {
174 template = "INVALID";
175 fail = 1;
176 }
177 if (!(passwd = strtok(NULL, ":")) || !*passwd ||
178 *passwd == '*' || *passwd == '!') {
179 passwd = AUTH_DUMMY_SALT;
180 fail = 1;
181 }
182 if (!strtok(NULL, ":")) fail = 1;
183
184 if ((pw = getpwnam(template)))
185 memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
186 endpwent();
187
188 result = NULL;
189 if (!strcmp(crypt(pass, passwd), passwd) && !fail)
190 result = pw;
191
192 memset(auth, 0, sizeof(auth));
193
194 return result;
195 }
196
197 #endif
198