1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "passwd_common.h"
18 #include "apr_strings.h"
19 #include "apr_errno.h"
44 #define unlink _unlink
49 static int generate_salt(char *s, size_t size, const char **errstr,
52 unsigned char rnd[32];
53 static const char itoa64[] =
54 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
56 unsigned int val = 0, bits = 0;
60 if (n > sizeof(rnd)) {
61 apr_file_printf(errfile, "generate_salt(): BUG: Buffer too small");
64 rv = apr_generate_random_bytes(rnd, n);
66 *errstr = apr_psprintf(pool, "Unable to generate random bytes: %pm",
73 val |= (rnd[n++] << bits);
76 *s++ = itoa64[val & 0x3f];
85 void putline(apr_file_t *f, const char *l)
88 rv = apr_file_puts(l, f);
89 if (rv != APR_SUCCESS) {
90 apr_file_printf(errfile, "Error writing temp file: %pm", &rv);
96 int get_password(struct passwd_ctx *ctx)
98 if (ctx->passwd_src == PW_STDIN) {
100 apr_file_t *file_stdin;
102 if (apr_file_open_stdin(&file_stdin, ctx->pool) != APR_SUCCESS) {
103 ctx->errstr = "Unable to read from stdin.";
106 if (apr_file_read_full(file_stdin, buf, ctx->out_len - 1,
108 || nread == ctx->out_len - 1) {
112 if (nread >= 1 && buf[nread-1] == '\n') {
114 if (nread >= 2 && buf[nread-2] == '\r')
117 apr_file_close(file_stdin);
120 char buf[MAX_STRING_LEN + 1];
121 apr_size_t bufsize = sizeof(buf);
122 if (apr_password_get("New password: ", ctx->out, &ctx->out_len) != 0)
124 apr_password_get("Re-type new password: ", buf, &bufsize);
125 if (strcmp(ctx->out, buf) != 0) {
126 ctx->errstr = "password verification error";
127 memset(ctx->out, '\0', ctx->out_len);
128 memset(buf, '\0', sizeof(buf));
129 return ERR_PWMISMATCH;
131 memset(buf, '\0', sizeof(buf));
136 ctx->errstr = apr_psprintf(ctx->pool,
137 "password too long (>%" APR_SIZE_T_FMT ")",
143 * Make a password record from the given information. A zero return
144 * indicates success; on failure, ctx->errstr points to the error message.
146 int mkhash(struct passwd_ctx *ctx)
149 char pwin[MAX_STRING_LEN];
153 #if CRYPT_ALGO_SUPPORTED
157 if (ctx->cost != 0 && ctx->alg != ALG_BCRYPT) {
158 apr_file_printf(errfile,
159 "Warning: Ignoring -C argument for this algorithm." NL);
162 if (ctx->passwd != NULL) {
166 if ((ret = get_password(ctx)) != 0)
173 /* XXX out >= 28 + strlen(sha1) chars - fixed len SHA */
174 apr_sha1_base64(pw, strlen(pw), ctx->out);
178 ret = generate_salt(salt, 8, &ctx->errstr, ctx->pool);
181 rv = apr_md5_encode(pw, salt, ctx->out, ctx->out_len);
182 if (rv != APR_SUCCESS) {
183 ctx->errstr = apr_psprintf(ctx->pool,
184 "could not encode password: %pm", &rv);
190 /* XXX this len limitation is not in sync with any HTTPd len. */
191 apr_cpystrn(ctx->out, pw, ctx->out_len);
194 #if CRYPT_ALGO_SUPPORTED
196 ret = generate_salt(salt, 8, &ctx->errstr, ctx->pool);
199 cbuf = crypt(pw, salt);
201 rv = APR_FROM_OS_ERROR(errno);
202 ctx->errstr = apr_psprintf(ctx->pool, "crypt() failed: %pm", &rv);
203 ret = ERR_PWMISMATCH;
207 apr_cpystrn(ctx->out, cbuf, ctx->out_len - 1);
208 if (strlen(pw) > 8) {
209 char *truncpw = strdup(pw);
211 if (!strcmp(ctx->out, crypt(truncpw, salt))) {
212 apr_file_printf(errfile, "Warning: Password truncated to 8 "
213 "characters by CRYPT algorithm." NL);
215 memset(truncpw, '\0', strlen(pw));
219 #endif /* CRYPT_ALGO_SUPPORTED */
221 #if BCRYPT_ALGO_SUPPORTED
223 rv = apr_generate_random_bytes((unsigned char*)salt, 16);
224 if (rv != APR_SUCCESS) {
225 ctx->errstr = apr_psprintf(ctx->pool, "Unable to generate random "
232 ctx->cost = BCRYPT_DEFAULT_COST;
233 rv = apr_bcrypt_encode(pw, ctx->cost, (unsigned char*)salt, 16,
234 ctx->out, ctx->out_len);
235 if (rv != APR_SUCCESS) {
236 ctx->errstr = apr_psprintf(ctx->pool, "Unable to encode with "
238 ret = ERR_PWMISMATCH;
242 #endif /* BCRYPT_ALGO_SUPPORTED */
245 apr_file_printf(errfile, "mkhash(): BUG: invalid algorithm %d",
249 memset(pw, '\0', strlen(pw));
253 int parse_common_options(struct passwd_ctx *ctx, char opt,
258 ctx->passwd_src = PW_ARG;
261 ctx->passwd_src = PW_STDIN;
264 ctx->alg = ALG_APMD5;
267 ctx->alg = ALG_APSHA;
270 ctx->alg = ALG_PLAIN;
271 #if !PLAIN_ALGO_SUPPORTED
272 /* Backward compatible behavior: Just print a warning */
273 apr_file_printf(errfile,
274 "Warning: storing passwords as plain text might just "
275 "not work on this platform." NL);
279 #if CRYPT_ALGO_SUPPORTED
280 ctx->alg = ALG_CRYPT;
282 /* Backward compatible behavior: Use MD5. OK since MD5 is more secure */
283 apr_file_printf(errfile,
284 "Warning: CRYPT algorithm not supported on this "
286 "Automatically using MD5 format." NL);
287 ctx->alg = ALG_APMD5;
291 #if BCRYPT_ALGO_SUPPORTED
292 ctx->alg = ALG_BCRYPT;
294 /* Don't fall back to something less secure */
295 ctx->errstr = "BCRYPT algorithm not supported on this platform";
296 return ERR_ALG_NOT_SUPP;
301 long num = strtol(opt_arg, &endptr, 10);
302 if (*endptr != '\0' || num <= 0) {
303 ctx->errstr = "argument to -C must be a positive integer";
310 apr_file_printf(errfile,
311 "parse_common_options(): BUG: invalid option %c",