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