1 /*----------------------------------------------------------------------------*/
2 /* Xymon webpage generator tool.                                              */
3 /*                                                                            */
4 /* Copyright (C) 2004-2011 Henrik Storner <henrik@storner.dk>                 */
5 /*                                                                            */
6 /* This program is released under the GNU General Public License (GPL),       */
7 /* version 2. See the file "COPYING" for details.                             */
8 /*                                                                            */
9 /*----------------------------------------------------------------------------*/
10 
11 static char rcsid[] = "$Id: chpasswd.c 6588 2010-11-14 17:21:19Z storner $";
12 
13 #include <stdio.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include <sys/wait.h>
17 #include <unistd.h>
18 
19 #include "libxymon.h"
20 
errormsg(int status,char * msg)21 static void errormsg(int status, char *msg)
22 {
23 	printf("Status: %d\n", status);
24 	printf("Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE"));
25 	printf("<html><head><title>Invalid request</title></head>\n");
26 	printf("<body>%s</body></html>\n", msg);
27 	exit(1);
28 }
29 
idcompare(const void * p1,const void * p2)30 static int idcompare(const void *p1, const void *p2)
31 {
32 	return strcmp(* (char * const *) p1, * (char * const *) p2);
33 }
34 
35 #define ACT_NONE 0
36 #define ACT_DELETE 2
37 #define ACT_UPDATE 3
38 
39 char *adduser_name = NULL;
40 char *adduser_password = NULL;
41 char *adduser_password1 = NULL;
42 char *adduser_password2 = NULL;
43 char *deluser_name = NULL;
44 
parse_query(void)45 int parse_query(void)
46 {
47 	cgidata_t *cgidata, *cwalk;
48 	int returnval = ACT_NONE;
49 
50 	cgidata = cgi_request();
51 	if (cgi_method != CGI_POST) return ACT_NONE;
52 
53 	if (cgidata == NULL) errormsg(400, cgi_error());
54 
55 	cwalk = cgidata;
56 	while (cwalk) {
57 		/*
58 		 * cwalk->name points to the name of the setting.
59 		 * cwalk->value points to the value (may be an empty string).
60 		 */
61 
62 		if (strcmp(cwalk->name, "USERNAME") == 0) {
63 			adduser_name = cwalk->value;
64 		}
65 		else if (strcmp(cwalk->name, "PASSWORD") == 0) {
66 			adduser_password = cwalk->value;
67 		}
68 		else if (strcmp(cwalk->name, "PASSWORD1") == 0) {
69 			adduser_password1 = cwalk->value;
70 		}
71 		else if (strcmp(cwalk->name, "PASSWORD2") == 0) {
72 			adduser_password2 = cwalk->value;
73 		}
74 		else if (strcmp(cwalk->name, "USERLIST") == 0) {
75 			deluser_name = cwalk->value;
76 		}
77 		else if (strcmp(cwalk->name, "SendDelete") == 0) {
78 			returnval = ACT_DELETE;
79 		}
80 		else if (strcmp(cwalk->name, "SendUpdate") == 0) {
81 			returnval = ACT_UPDATE;
82 		}
83 
84 		cwalk = cwalk->next;
85 	}
86 
87 	/* We only want to accept posts from certain pages */
88 
89 	if (returnval != ACT_NONE) {
90 		char cgisource[1024]; char *p;
91 		p = csp_header("chpasswd"); if (p) fprintf(stdout, "%s", p);
92 		snprintf(cgisource, sizeof(cgisource), "%s/%s", xgetenv("SECURECGIBINURL"), "chpasswd");
93 		if (!cgi_refererok(cgisource)) { fprintf(stdout, "Location: %s.sh?\n\n", cgisource); return 0; }
94 	}
95 
96 	return returnval;
97 }
98 
main(int argc,char * argv[])99 int main(int argc, char *argv[])
100 {
101 	int argi, event;
102 	char *envarea = NULL;
103 	char *hffile = "chpasswd";
104 	SBUF_DEFINE(passfile);
105 	FILE *fd;
106 	char *infomsg = NULL;
107 	char *loggedinuser = NULL;
108 
109 	for (argi = 1; (argi < argc); argi++) {
110 		if (argnmatch(argv[argi], "--env=")) {
111 			char *p = strchr(argv[argi], '=');
112 			loadenv(p+1, envarea);
113 		}
114 		else if (argnmatch(argv[argi], "--area=")) {
115 			char *p = strchr(argv[argi], '=');
116 			envarea = strdup(p+1);
117 		}
118 		else if (strcmp(argv[argi], "--debug") == 0) {
119 			debug = 1;
120 		}
121 		else if (argnmatch(argv[argi], "--passwdfile=")) {
122 			char *p = strchr(argv[argi], '=');
123 			passfile = strdup(p+1);
124 		}
125 	}
126 
127 	if (passfile == NULL) {
128 		SBUF_MALLOC(passfile, strlen(xgetenv("XYMONHOME")) + 20);
129 		snprintf(passfile, passfile_buflen, "%s/etc/xymonpasswd", xgetenv("XYMONHOME"));
130 	}
131 
132 	loggedinuser = getenv("REMOTE_USER");
133         if (!loggedinuser) errormsg(401, "User authentication must be enabled and you must be logged in to use this CGI");
134 
135 	event = parse_query();
136 
137 	if (adduser_name && !issimpleword(adduser_name)) {
138 		event = ACT_NONE;
139 		adduser_name = strdup("");
140 		infomsg = "<strong><big><font color='#FF0000'>Invalid USERNAME. Letters, numbers, dashes, and periods only.</font></big></strong>\n";
141 	}
142 
143 	switch (event) {
144 	  case ACT_NONE:	/* Show the form */
145 		break;
146 
147 	  case ACT_UPDATE:	/* Change a user password*/
148 		{
149 			char *cmd;
150 			int n, ret;
151 
152 			if ( (strlen(loggedinuser) == 0) || (strlen(loggedinuser) != strlen(adduser_name)) || (strcmp(loggedinuser, adduser_name) != 0) ) {
153 				infomsg = "Username mismatch! You may only change your own password.";
154 				break;
155 			}
156 
157 			if ( (strlen(adduser_name) == 0)) {
158 				infomsg = "User not logged in";
159 			}
160 			else if ( (strlen(adduser_password1) == 0) || (strlen(adduser_password2) == 0)) {
161 				infomsg = "New password cannot be blank";
162 			}
163 			else if (strcmp(adduser_password1, adduser_password2) != 0) {
164 				infomsg = "New passwords don't match";
165 			}
166 			else if (strlen(adduser_name) != strspn(adduser_name,"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.,@/=^") ) {
167 				infomsg = "Username has invalid characters!";
168 			}
169 			else if (strlen(adduser_password1) != strspn(adduser_password1,"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.,@/=^") ) {
170 				infomsg = "Password has invalid characters! Use alphanumerics and/or _ - . , @ / = ^";
171 			}
172 			else {
173 				pid_t childpid;
174 				int n, ret;
175 
176 				childpid = fork();
177 				if (childpid < 0) {
178 				        /* Fork failed */
179 				        errprintf("Could not fork child\n");
180 				        exit(1);
181 				}
182 				else if (childpid == 0) {
183 				        /* child */
184 				        char *cmd;
185 				        char **cmdargs;
186 
187 				        cmdargs = (char **) calloc(4 + 2, sizeof(char *));
188 				        cmdargs[0] = cmd = strdup("htpasswd");
189 				        cmdargs[1] = "-bv";
190 				        cmdargs[2] = strdup(passfile);
191 				        cmdargs[3] = strdup(adduser_name);
192 				        cmdargs[4] = strdup(adduser_password);
193 				        cmdargs[5] = '\0';
194 
195 				        execvp(cmd, cmdargs);
196 				        exit(127);
197 				}
198 
199 				/* parent waits for htpasswd to finish */
200 				if ((waitpid(childpid, &n, 0) == -1) || (WEXITSTATUS(n) != 0)) {
201 					infomsg = "Existing Password incorrect";
202 					break;
203 				}
204 
205 				childpid = fork();
206 				if (childpid < 0) {
207 				        /* Fork failed */
208 				        errprintf("Could not fork child\n");
209 				        exit(1);
210 				}
211 				else if (childpid == 0) {
212 				        /* child */
213 				        char *cmd;
214 				        char **cmdargs;
215 
216 				        cmdargs = (char **) calloc(4 + 2, sizeof(char *));
217 				        cmdargs[0] = cmd = strdup("htpasswd");
218 				        cmdargs[1] = "-b";
219 				        cmdargs[2] = strdup(passfile);
220 				        cmdargs[3] = strdup(adduser_name);
221 				        cmdargs[4] = strdup(adduser_password1);
222 				        cmdargs[5] = '\0';
223 
224 				        execvp(cmd, cmdargs);
225 				        exit(127);
226 				}
227 
228 				/* parent waits for htpasswd to finish */
229 				if ((waitpid(childpid, &n, 0) == -1) || (WEXITSTATUS(n) != 0)) {
230 					infomsg = "Update FAILED";
231 
232 				}
233 				else {
234 					infomsg = "<strong><big>Password changed</big></strong>\n";
235 				}
236 			}
237 		}
238 		break;
239 
240 	}
241 
242 	fprintf(stdout, "Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE"));
243 
244 	showform(stdout, hffile, "chpasswd_form", COL_BLUE, getcurrenttime(NULL), infomsg, NULL);
245 
246 	return 0;
247 }
248 
249