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