1 /* makeweb.c - let a user create a web subdirectory
2 **
3 ** Copyright � 1995 by Jef Poskanzer <jef@mail.acme.com>.
4 ** All rights reserved.
5 **
6 ** Redistribution and use in source and binary forms, with or without
7 ** modification, are permitted provided that the following conditions
8 ** are met:
9 ** 1. Redistributions of source code must retain the above copyright
10 **    notice, this list of conditions and the following disclaimer.
11 ** 2. Redistributions in binary form must reproduce the above copyright
12 **    notice, this list of conditions and the following disclaimer in the
13 **    documentation and/or other materials provided with the distribution.
14 **
15 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 ** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 ** SUCH DAMAGE.
26 */
27 
28 /* This is intended to be installed setgid to a group that has
29 ** write access to the system web directory.  It allows any user
30 ** to create a subdirectory there.  It also makes a symbolic link
31 ** in the user's home directory pointing at the new web subdir.
32 */
33 
34 
35 #include "../config.h"
36 
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <pwd.h>
42 #include <errno.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 
46 
47 #define LINK "public_html"
48 
49 static char* argv0;
50 
51 static void
usage(void)52 usage( void )
53 {
54     fprintf( stderr, "usage: %s [-d webdir]\n", argv0 );
55 }
56 
57 static void
check_room(int size,int len)58 check_room( int size, int len )
59     {
60     if ( len > size )
61 	{
62 	(void) fprintf( stderr, "%s: internal error, out of room\n", argv0 );
63 	exit( 1 );
64 	}
65     }
66 
67 
68 static void
end_with_slash(char * str)69 end_with_slash( char* str )
70     {
71     if ( str[strlen( str ) - 1] != '/' )
72 	(void) strcat( str, "/" );
73     }
74 
75 
76 static void
check_dir(char * dirname,uid_t uid,gid_t gid)77 check_dir( char* dirname, uid_t uid, gid_t gid )
78     {
79     struct stat sb;
80 
81     /* Check the directory. */
82     if ( stat( dirname, &sb ) < 0 )
83 	{
84 	if ( errno != ENOENT )
85 	    {
86 	    perror( dirname );
87 	    exit( 1 );
88 	    }
89 	/* Doesn't exist.  Try to make it. */
90 	if ( mkdir( dirname, 0755 ) < 0 )
91 	    {
92 	    if ( errno == ENOENT )
93 		(void) printf( "\
94 Some part of the path %s does not exist.\n\
95 This is probably a configuration error.\n", dirname );
96 	    else
97 		perror( dirname );
98 	    exit( 1 );
99 	    }
100 	(void) printf( "Created web directory %s\n", dirname );
101 	/* Try to change the group of the new dir to the user's group. */
102 	(void) chown( dirname, -1, gid );
103 	}
104     else
105 	{
106 	/* The directory already exists.  Well, check that it is in
107 	** fact a directory.
108 	*/
109 	if ( ! S_ISDIR( sb.st_mode ) )
110 	    {
111 	    (void) printf(
112 		"%s already exists but is not a directory!\n", dirname );
113 	    exit( 1 );
114 	    }
115 	if ( sb.st_uid != uid )
116 	    {
117 	    (void) printf(
118 		"%s already exists but you don't own it!\n", dirname );
119 	    exit( 1 );
120 	    }
121 	(void) printf( "Web directory %s already existed.\n", dirname );
122 	}
123     }
124 
125 
126 int
main(int argc,char ** argv)127 main( int argc, char** argv )
128     {
129     char* webdir;
130     char* prefix;
131     struct passwd* pwd;
132     char* username;
133     char* homedir;
134     int opts;
135     char dirname[5000];
136     char linkname[5000];
137     char linkbuf[5000];
138     struct stat sb;
139 
140     argv0 = argv[0];
141 
142 #ifndef TILDE_MAP_2
143     webdir = WEBDIR;
144 #endif /* TILDE_MAP_2 */
145 
146     if ( (opts = getopt(argc, argv, "d:h")) != -1 )
147     {
148 	switch (opts)
149 	{
150 	    case 'd': webdir = strdup(optarg); break;
151 	    case 'h':
152 	    default: usage(); return 1; break;
153 	}
154     }
155 
156     pwd = getpwuid( getuid() );
157     if ( pwd == (struct passwd*) 0 )
158 	{
159 	(void) fprintf( stderr, "%s: can't find your username\n", argv0 );
160 	exit( 1 );
161 	}
162     username = pwd->pw_name;
163     homedir = pwd->pw_dir;
164 
165 #ifdef TILDE_MAP_2
166 
167     /* All we have to do for the TILDE_MAP_2 case is make sure there's
168     ** a public_html subdirectory.
169     */
170     check_room(
171 	sizeof(dirname), strlen( homedir ) + strlen( TILDE_MAP_2 ) + 2 );
172     (void) strcpy( dirname, homedir );
173     end_with_slash( dirname );
174     (void) strcat( dirname, TILDE_MAP_2 );
175 
176     check_dir( dirname, pwd->pw_uid, pwd->pw_gid );
177 
178 #else /* TILDE_MAP_2 */
179 
180 #ifdef TILDE_MAP_1
181     prefix = TILDE_MAP_1;
182 #else /* TILDE_MAP_1 */
183     prefix = "";
184 #endif /* TILDE_MAP_1 */
185 
186     /* Assemble the directory name.  Be paranoid cause we're sgid. */
187     check_room(
188 	sizeof(dirname),
189 	strlen( webdir ) + strlen( prefix ) + strlen( username ) + 3 );
190     (void) strcpy( dirname, webdir );
191     end_with_slash( dirname );
192     if ( strlen( prefix ) != 0 )
193 	{
194 	(void) strcat( dirname, prefix );
195 	end_with_slash( dirname );
196 	}
197     (void) strcat( dirname, username );
198 
199     /* Assemble the link name. */
200     check_room( sizeof(linkname), strlen( homedir ) + strlen( LINK ) + 2 );
201     (void) strcpy( linkname, homedir );
202     end_with_slash( linkname );
203     (void) strcat( linkname, LINK );
204 
205     check_dir( dirname, pwd->pw_uid, pwd->pw_gid );
206 
207     /* Check the symlink. */
208     try_link_again: ;
209     if ( lstat( linkname, &sb ) < 0 )
210 	{
211 	if ( errno != ENOENT )
212 	    {
213 	    perror( linkname );
214 	    exit( 1 );
215 	    }
216 	/* Doesn't exist.  Try to make it. */
217 	if ( symlink( dirname, linkname ) < 0 )
218 	    {
219 	    if ( errno == ENOENT )
220 		(void) printf( "\
221 Some part of the path %s does not exist.\n\
222 This is probably a configuration error.\n", linkname );
223 	    else
224 		perror( linkname );
225 	    exit( 1 );
226 	    }
227 	(void) printf( "Created symbolic link %s\n", linkname );
228 	}
229     else
230 	{
231 	/* The link already exists.  Well, check that it is in
232 	** fact a link.
233 	*/
234 	if ( ! S_ISLNK( sb.st_mode ) )
235 	    {
236 	    (void) printf( "\
237 %s already exists but is not a\n\
238 symbolic link!  Perhaps you have a real web subdirectory in your\n\
239 home dir from a previous web server configuration?  You may have\n\
240 to rename it, run %s again, and then copy in the old\n\
241 contents.\n", linkname, argv0 );
242 	    exit( 1 );
243 	    }
244 	/* Check the existing link's contents. */
245 	if ( readlink( linkname, linkbuf, sizeof(linkbuf) ) < 0 )
246 	    {
247 	    perror( linkname );
248 	    exit( 1 );
249 	    }
250 	if ( strcmp( dirname, linkbuf ) == 0 )
251 	    (void) printf( "Symbolic link %s already existed.\n", linkname );
252 	else
253 	    {
254 	    (void) printf( "\
255 Symbolic link %s already existed\n\
256 but it points to the wrong place!  Attempting to remove and\n\
257 recreate it.\n", linkname );
258 	    if ( unlink( linkname ) < 0 )
259 		{
260 		perror( linkname );
261 		exit( 1 );
262 		}
263 	    goto try_link_again;
264 	    }
265 	}
266 #endif /* TILDE_MAP_2 */
267 
268     exit( 0 );
269     }
270