1 //
2 // aegis - project change supervisor
3 // Copyright (C) 2004-2006, 2008 Peter Miller
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program. If not, see
17 // <http://www.gnu.org/licenses/>.
18 //
19
20 #include <common/ac/crypt.h>
21 #include <common/ac/errno.h>
22 #include <common/ac/grp.h>
23 #include <common/ac/pwd.h>
24 #include <common/ac/shadow.h>
25 #include <common/ac/string.h>
26 #include <common/ac/unistd.h>
27 #include <common/ac/sys/types.h>
28 #include <common/ac/sys/stat.h> // for umask
29
30 #include <common/env.h>
31 #include <libaegis/os.h>
32 #include <aecvsserver/response/error.h>
33 #include <aecvsserver/response/hate.h>
34 #include <aecvsserver/response/love.h>
35 #include <aecvsserver/server/password.h>
36 #include <aecvsserver/server/private.h>
37 #include <aecvsserver/server/simple.h>
38 #include <aecvsserver/scramble.h>
39
40
41 struct server_password_ty
42 {
43 server_ty inherited;
44 server_ty *simple;
45 };
46
47
48 static void
destructor(server_ty * sp)49 destructor(server_ty *sp)
50 {
51 server_password_ty *spp;
52
53 spp = (server_password_ty *)sp;
54 if (spp->simple)
55 {
56 server_delete(spp->simple);
57 spp->simple = 0;
58 }
59 }
60
61
62 static struct passwd *
check_system_password(const char * username,const char * password)63 check_system_password(const char *username, const char *password)
64 {
65 struct passwd *pw;
66 char *cp;
67
68 //
69 // Make sure the user exists.
70 //
71 pw = getpwnam(username);
72 if (!pw)
73 return 0;
74
75 //
76 // Don't even think about allowing root.
77 //
78 if (pw->pw_uid == 0)
79 return 0;
80
81 //
82 // If this systejm has shadow passwords, grab the shadow password.
83 //
84 #ifdef HAVE_GETSPNAM
85 {
86 struct spwd *spw;
87
88 spw = getspnam(username);
89 if (spw)
90 pw->pw_passwd = spw->sp_pwdp;
91 }
92 #endif
93
94 //
95 // Allow for dain bramaged HPUX passwd aging
96 // - Basically, HPUX adds a comma and some data
97 // about whether the passwd has expired or not
98 // on the end of the passwd field.
99 // - This code replaces the ',' with '\0'.
100 //
101 // I'm guessing that HPUX WANTED other systems to think the password
102 // was wrong so logins would fail if the system didn't handle expired
103 // passwds and the passwd might be expired.
104 //
105 cp = pw->pw_passwd;
106 while (*cp)
107 {
108 if (*cp == ',')
109 {
110 *cp = 0;
111 break;
112 }
113 ++cp;
114 }
115
116 //
117 // If the user has no password, we are done.
118 //
119 if (pw->pw_passwd[0] == 0)
120 {
121 if (password[0])
122 return 0;
123 return pw;
124 }
125
126 //
127 // Check the password.
128 //
129 #if HAVE_CRYPT
130 if (0 != strcmp(pw->pw_passwd, crypt(password, pw->pw_passwd)))
131 return 0;
132
133 //
134 // Report success by return the user data.
135 //
136 return pw;
137 #else
138 return 0;
139 #endif
140 }
141
142
143 static void
run(server_ty * sp)144 run(server_ty *sp)
145 {
146 server_password_ty *spp = (server_password_ty *)sp;
147
148 //
149 // cvsclient.texi:
150 // The client connects, and sends the following:
151 //
152 // + the string BEGIN AUTH REQUEST, a linefeed,
153 // + the cvs root, a linefeed,
154 // + the username, a linefeed,
155 // + the password trivially encoded (see Password scrambling), a
156 // linefeed,
157 // + the string END AUTH REQUEST, and a linefeed.
158 //
159 bool verify = false;
160 bool ok = false;
161 nstring s;
162 if (!server_getline(sp, s))
163 {
164 protocol_failure:
165 server_e(sp, "authentication protocol error");
166 auth_failure:
167 server_response_queue(sp, new response_hate());
168 server_response_flush(sp);
169 return;
170 }
171 if (!strcmp(s.c_str(), "BEGIN GSSAPI REQUEST"))
172 {
173 server_error(sp, "GSSAPI authentication not supported by this server");
174 goto auth_failure;
175 }
176 if (!strcmp(s.c_str(), "BEGIN VERIFICATION REQUEST"))
177 {
178 ok = true;
179 verify = true;
180 }
181 else
182 ok = !strcmp(s.c_str(), "BEGIN AUTH REQUEST");
183 if (!ok)
184 goto protocol_failure;
185
186 if (!server_getline(sp, s))
187 goto protocol_failure;
188 ok = !strcmp(s.c_str(), ROOT_PATH);
189 if (!ok)
190 {
191 //
192 // cvsclient.texi:
193 // "The client must send the identical string for cvs root both
194 // here and later in the Root request of the cvs protocol itself.
195 // Servers are encouraged to enforce this restriction."
196 //
197 // We only allow one Root specification, exactly ROOT_PATH,
198 // and we check it in both places.
199 //
200 server_e(sp, "%s: no such repository", s.c_str());
201 goto auth_failure;
202 }
203
204 nstring user_name;
205 if (!server_getline(sp, user_name))
206 goto protocol_failure;
207 nstring scrambled_password;
208 if (!server_getline(sp, scrambled_password))
209 goto protocol_failure;
210
211 if (!server_getline(sp, s))
212 goto protocol_failure;
213 if (verify)
214 ok = !strcmp(s.c_str(), "END VERIFICATION REQUEST");
215 else
216 ok = !strcmp(s.c_str(), "END AUTH REQUEST");
217 if (!ok)
218 goto protocol_failure;
219
220 //
221 // Check that the user name and password are acceptable.
222 //
223 nstring password = descramble(scrambled_password);
224 struct passwd *pw =
225 check_system_password(user_name.c_str(), password.c_str());
226 if (!pw)
227 goto auth_failure;
228
229 //
230 // Report success.
231 //
232 server_response_queue(sp, new response_love());
233 server_response_flush(sp);
234
235 if (!verify)
236 {
237 #if HAVE_INITGROUPS
238 if
239 (
240 initgroups (pw->pw_name, pw->pw_gid) < 0
241 #ifdef EPERM
242 &&
243 //
244 // Note that initgroups() only works as root. But we do
245 // still want to report ENOMEM and whatever other errors
246 // initgroups() might dish up.
247 //
248 errno != EPERM
249 #endif
250 )
251 {
252 int err;
253
254 //
255 // This could be a warning, but I'm not sure I see the point
256 // in doing that instead of an error given that it would happen
257 // on every connection. We could log it somewhere and not tell
258 // the user. But at least for now make it an error.
259 //
260 err = errno;
261 server_e(sp, "initgroups failed: %s", strerror(err));
262 goto auth_failure;
263 }
264 #endif // HAVE_INITGROUPS
265
266 //
267 // Drop user privilege level.
268 //
269 if (setuid(pw->pw_uid) < 0)
270 {
271 int err;
272
273 err = errno;
274 server_e(sp, "setuid failed: %s", strerror(err));
275 goto auth_failure;
276 }
277
278 //
279 // Let libaegis.a know we have changed uid.
280 //
281 os_become_reinit_mortal();
282
283 //
284 // Set a sensable umask.
285 //
286 umask(DEFAULT_UMASK);
287
288 //
289 // Set some environment variables.
290 //
291 env_set("LOGNAME", user_name.c_str());
292 env_set("USER", user_name.c_str());
293
294 //
295 // Now run the simple server.
296 //
297 if (!spp->simple)
298 spp->simple = server_simple_new(sp->np);
299 server_run(spp->simple);
300 }
301 }
302
303
304 static server_method_ty vtbl =
305 {
306 sizeof(server_password_ty),
307 destructor,
308 run,
309 "password",
310 };
311
312
313 server_ty *
server_password_new(net_ty * np)314 server_password_new(net_ty *np)
315 {
316 server_ty *sp;
317 server_password_ty *spp;
318
319 sp = server_new(&vtbl, np);
320 spp = (server_password_ty *)sp;
321 spp->simple = 0;
322 return sp;
323 }
324