1 /* saslpasswd.c -- SASL password setting program
5 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
19 * 3. The name "Carnegie Mellon University" must not be used to
20 * endorse or promote products derived from this software without
21 * prior written permission. For permission or any other legal
22 * details, please contact
23 * Office of Technology Transfer
24 * Carnegie Mellon University
26 * Pittsburgh, PA 15213-3890
27 * (412) 268-4387, fax: (412) 268-7395
28 * tech-transfer@andrew.cmu.edu
30 * 4. Redistributions of any form whatsoever must retain the following
32 * "This product includes software developed by Computing Services
33 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
35 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
36 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
37 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
38 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
39 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
40 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
41 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
52 /* perror can't be used on Windows system calls, so we define a new macro to underline this */
53 #define p_oserror(str) perror(str)
61 __declspec(dllimport) char *optarg;
62 __declspec(dllimport) int optind;
64 /* perror can't be used on Windows system calls, so we define a new macro to underline this */
65 void p_oserror (const char *string);
71 char myhostname[1025];
73 #define PW_BUF_SIZE 2048
75 static const char build_ident[] = "$Build: saslpasswd " PACKAGE "-" VERSION " $";
77 const char *progname = NULL;
78 char *sasldb_path = NULL;
82 /* This is almost like _plug_get_error_message(), but uses malloc */
83 char * _get_error_message (
91 FORMAT_MESSAGE_ALLOCATE_BUFFER |
92 FORMAT_MESSAGE_FROM_SYSTEM |
93 FORMAT_MESSAGE_IGNORE_INSERTS,
96 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
102 return_value = strdup (lpMsgBuf);
104 LocalFree( lpMsgBuf );
105 return (return_value);
108 /* perror() like function that works on OS error codes returned by GetLastError() */
113 /* Try to match perror() behaviour:
114 string is printed first, followed by a colon, then by the system error message
115 for the last library call that produced the error, and finally by a newline
116 character. If string is a null pointer or a pointer to a null string, perror
117 prints only the system error message.
119 if (message && *message) {
120 fprintf (stderr, "%s: %s\n", message, _get_error_message(GetLastError()));
122 fprintf (stderr, "%s\n", _get_error_message(GetLastError()));
127 void read_password(const char *prompt,
132 char buf[PW_BUF_SIZE];
134 struct termios ts, nts;
138 DWORD n_read, fdwMode, fdwOldMode;
139 hStdin = GetStdHandle(STD_INPUT_HANDLE);
140 if (hStdin == INVALID_HANDLE_VALUE) {
147 fputs(prompt, stdout);
150 tcgetattr(STDIN_FILENO, &ts);
152 nts.c_lflag &= ~(ECHO | ECHOE | ECHOK
163 nts.c_lflag |= ICANON | ECHONL;
164 tcsetattr(STDIN_FILENO, TCSAFLUSH, &nts);
166 if (! GetConsoleMode(hStdin, &fdwOldMode)) {
170 fdwMode = fdwOldMode & ~ENABLE_ECHO_INPUT;
171 if (! SetConsoleMode(hStdin, fdwMode)) {
179 n_read = read(STDIN_FILENO, buf, PW_BUF_SIZE);
182 if (! ReadFile(hStdin, buf, PW_BUF_SIZE, &n_read, NULL)) {
191 tcsetattr(STDIN_FILENO, TCSANOW, &ts);
192 if (0 < n_read && buf[n_read - 1] != '\n') {
193 /* if we didn't end with a \n, echo one */
198 SetConsoleMode(hStdin, fdwOldMode);
204 if (0 < n_read && buf[n_read - 1] == '\n') /* if we ended with a \n */
205 n_read--; /* remove it */
208 /*WIN32 will have a CR in the buffer also*/
209 if (0 < n_read && buf[n_read - 1] == '\r') /* if we ended with a \r */
210 n_read--; /* remove it */
213 *password = malloc(n_read + 1);
215 /* Can use perror() here even on Windows, as malloc is in std C library */
220 memcpy(*password, buf, n_read);
221 (*password)[n_read] = '\0'; /* be nice... */
225 void exit_sasl(int result, const char *errstr) __attribute__((noreturn));
228 exit_sasl(int result, const char *errstr)
230 (void)fprintf(stderr, errstr ? "%s: %s: %s\n" : "%s: %s\n",
232 sasl_errstring(result, NULL, NULL),
234 exit(result < 0 ? -result : result);
237 int good_getopt(void *context __attribute__((unused)),
238 const char *plugin_name __attribute__((unused)),
243 if (sasldb_path && !strcmp(option, "sasldb_path")) {
244 *result = sasldb_path;
246 *len = strlen(sasldb_path);
253 static struct sasl_callback goodsasl_cb[] = {
254 { SASL_CB_GETOPT, &good_getopt, NULL },
255 { SASL_CB_LIST_END, NULL, NULL }
259 main(int argc, char *argv[])
261 int flag_pipe = 0, flag_create = 0, flag_disable = 0, flag_error = 0;
262 int flag_nouserpass = 0;
264 char *userid, *password, *verify;
265 unsigned passlen, verifylen;
266 const char *errstr = NULL;
269 char *user_domain = NULL;
270 char *appname = "saslpasswd";
271 const char *sasl_implementation;
278 /* initialize winsock */
281 result = WSAStartup( MAKEWORD(2, 0), &wsaData );
283 exit_sasl(SASL_FAIL, "WSAStartup");
287 memset(myhostname, 0, sizeof(myhostname));
288 result = gethostname(myhostname, sizeof(myhostname)-1);
289 if (result == -1) exit_sasl(SASL_FAIL, "gethostname");
292 progname = "saslpasswd";
294 progname = strrchr(argv[0], HIER_DELIMITER);
301 while ((c = getopt(argc, argv, "vpcdnf:u:a:h?")) != EOF)
322 user_domain = optarg;
325 sasldb_path = optarg;
329 if (strchr(optarg, '/') != NULL) {
330 (void)fprintf(stderr, "appname must not contain /\n");
335 sasl_version (&sasl_implementation, &libsasl_version);
336 libsasl_major = libsasl_version >> 24;
337 libsasl_minor = (libsasl_version >> 16) & 0xFF;
338 libsasl_step = libsasl_version & 0xFFFF;
340 (void)fprintf(stderr, "\nThis product includes software developed by Computing Services\n"
341 "at Carnegie Mellon University (http://www.cmu.edu/computing/).\n\n"
342 "Built against SASL API version %u.%u.%u\n"
343 "LibSasl version %u.%u.%u by \"%s\"\n",
344 SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP,
345 libsasl_major, libsasl_minor, libsasl_step, sasl_implementation);
353 if (optind != argc - 1)
357 (void)fprintf(stderr,
358 "\nThis product includes software developed by Computing Services\n"
359 "at Carnegie Mellon University (http://www.cmu.edu/computing/).\n\n"
360 "%s: usage: %s [-v] [-c [-p] [-n]] [-d] [-a appname] [-f sasldb] [-u DOM] userid\n"
361 "\t-p\tpipe mode -- no prompt, password read on stdin\n"
362 "\t-c\tcreate -- ask mechs to create the account\n"
363 "\t-d\tdisable -- ask mechs to disable/delete the account\n"
364 "\t-n\tno userPassword -- don't set plaintext userPassword property\n"
365 "\t \t (only set mechanism-specific secrets)\n"
366 "\t-f sasldb\tuse given file as sasldb\n"
367 "\t-a appname\tuse appname as application name\n"
368 "\t-u DOM\tuse DOM for user domain\n"
369 "\t-v\tprint version numbers and exit\n",
374 userid = argv[optind];
376 result = sasl_server_init(goodsasl_cb, appname);
377 if (result != SASL_OK)
378 exit_sasl(result, NULL);
380 result = sasl_server_new("sasldb",
388 if (result != SASL_OK)
389 exit_sasl(result, NULL);
392 if (! flag_pipe && ! isatty(STDIN_FILENO))
397 read_password("Password: ", flag_pipe, &password, &passlen);
400 read_password("Again (for verification): ", flag_pipe, &verify,
402 if (passlen != verifylen
403 || memcmp(password, verify, verifylen)) {
404 fprintf(stderr, "%s: passwords don't match; aborting\n",
406 exit(-(SASL_BADPARAM));
411 result = sasl_setpass(conn,
416 (flag_create ? SASL_SET_CREATE : 0)
417 | (flag_disable ? SASL_SET_DISABLE : 0)
418 | (flag_nouserpass ? SASL_SET_NOPLAIN : 0));
420 if (result != SASL_OK && !flag_disable)
421 exit_sasl(result, NULL);
423 struct propctx *propctx = NULL;
424 const char *delete_request[] = { "cmusaslsecretCRAM-MD5",
425 "cmusaslsecretDIGEST-MD5",
426 "cmusaslsecretPLAIN",
429 /* Either we were setting and succeeded or we were disabling and
430 failed. In either case, we want to wipe old entries */
432 /* Delete the possibly old entries */
433 /* We don't care if these fail */
434 propctx = prop_new(0);
435 if (!propctx) ret = SASL_FAIL;
436 if (!ret) ret = prop_request(propctx, delete_request);
438 ret = prop_set(propctx, "cmusaslsecretCRAM-MD5", NULL, 0);
439 ret = prop_set(propctx, "cmusaslsecretDIGEST-MD5", NULL, 0);
440 ret = prop_set(propctx, "cmusaslsecretPLAIN", NULL, 0);
441 ret = sasl_auxprop_store(conn, propctx, userid);
443 if (propctx) prop_dispose(&propctx);
446 if (result != SASL_OK)
447 /* errstr is currently always NULL */
448 exit_sasl(result, errstr);