1/* saslpasswd.c -- SASL password setting program 2 * Rob Earhart 3 */ 4/* 5 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 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 17 * distribution. 18 * 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 25 * 5000 Forbes Avenue 26 * Pittsburgh, PA 15213-3890 27 * (412) 268-4387, fax: (412) 268-7395 28 * tech-transfer@andrew.cmu.edu 29 * 30 * 4. Redistributions of any form whatsoever must retain the following 31 * acknowledgment: 32 * "This product includes software developed by Computing Services 33 * at Carnegie Mellon University (http://www.cmu.edu/computing/)." 34 * 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. 42 */ 43 44#include <config.h> 45#include <stdio.h> 46#include <assert.h> 47 48#ifndef WIN32 49#include <termios.h> 50#include <unistd.h> 51 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) 54 55#else /* WIN32 */ 56 57#include <stdio.h> 58#include <io.h> 59 60#include <saslutil.h> 61__declspec(dllimport) char *optarg; 62__declspec(dllimport) int optind; 63 64/* perror can't be used on Windows system calls, so we define a new macro to underline this */ 65void p_oserror (const char *string); 66#endif /*WIN32*/ 67 68#include <sasl.h> 69#include <saslplug.h> 70 71char myhostname[1025]; 72 73#define PW_BUF_SIZE 2048 74 75static const char build_ident[] = "$Build: saslpasswd " PACKAGE "-" VERSION " $"; 76 77const char *progname = NULL; 78char *sasldb_path = NULL; 79 80#ifdef WIN32 81 82/* This is almost like _plug_get_error_message(), but uses malloc */ 83char * _get_error_message ( 84 DWORD error 85) 86{ 87 char * return_value; 88 LPVOID lpMsgBuf; 89 90 FormatMessage( 91 FORMAT_MESSAGE_ALLOCATE_BUFFER | 92 FORMAT_MESSAGE_FROM_SYSTEM | 93 FORMAT_MESSAGE_IGNORE_INSERTS, 94 NULL, 95 error, 96 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ 97 (LPTSTR) &lpMsgBuf, 98 0, 99 NULL 100 ); 101 102 return_value = strdup (lpMsgBuf); 103 104 LocalFree( lpMsgBuf ); 105 return (return_value); 106} 107 108/* perror() like function that works on OS error codes returned by GetLastError() */ 109void p_oserror ( 110 const char *message 111) 112{ 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. 118 */ 119 if (message && *message) { 120 fprintf (stderr, "%s: %s\n", message, _get_error_message(GetLastError())); 121 } else { 122 fprintf (stderr, "%s\n", _get_error_message(GetLastError())); 123 } 124} 125#endif /* WIN32 */ 126 127void read_password(const char *prompt, 128 int flag_pipe, 129 char ** password, 130 unsigned *passlen) 131{ 132 char buf[PW_BUF_SIZE]; 133#ifndef WIN32 134 struct termios ts, nts; 135 ssize_t n_read; 136#else 137 HANDLE hStdin; 138 DWORD n_read, fdwMode, fdwOldMode; 139 hStdin = GetStdHandle(STD_INPUT_HANDLE); 140 if (hStdin == INVALID_HANDLE_VALUE) { 141 p_oserror(progname); 142 exit(-(SASL_FAIL)); 143 } 144#endif /*WIN32*/ 145 146 if (! flag_pipe) { 147 fputs(prompt, stdout); 148 fflush(stdout); 149#ifndef WIN32 150 tcgetattr(STDIN_FILENO, &ts); 151 nts = ts; 152 nts.c_lflag &= ~(ECHO | ECHOE | ECHOK 153#ifdef ECHOCTL 154 | ECHOCTL 155#endif 156#ifdef ECHOPRT 157 | ECHOPRT 158#endif 159#ifdef ECHOKE 160 | ECHOKE 161#endif 162 ); 163 nts.c_lflag |= ICANON | ECHONL; 164 tcsetattr(STDIN_FILENO, TCSAFLUSH, &nts); 165#else 166 if (! GetConsoleMode(hStdin, &fdwOldMode)) { 167 p_oserror(progname); 168 exit(-(SASL_FAIL)); 169 } 170 fdwMode = fdwOldMode & ~ENABLE_ECHO_INPUT; 171 if (! SetConsoleMode(hStdin, fdwMode)) { 172 p_oserror(progname); 173 exit(-(SASL_FAIL)); 174 } 175#endif /*WIN32*/ 176 } 177 178#ifndef WIN32 179 n_read = read(STDIN_FILENO, buf, PW_BUF_SIZE); 180 if (n_read < 0) { 181#else 182 if (! ReadFile(hStdin, buf, PW_BUF_SIZE, &n_read, NULL)) { 183#endif /*WIN32*/ 184 185 p_oserror(progname); 186 exit(-(SASL_FAIL)); 187 } 188 189 if (! flag_pipe) { 190#ifndef WIN32 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 */ 194 putchar('\n'); 195 fflush(stdout); 196 } 197#else 198 SetConsoleMode(hStdin, fdwOldMode); 199 putchar('\n'); 200 fflush(stdout); 201#endif /*WIN32*/ 202 } 203 204 if (0 < n_read && buf[n_read - 1] == '\n') /* if we ended with a \n */ 205 n_read--; /* remove it */ 206 207#ifdef WIN32 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 */ 211#endif /*WIN32*/ 212 213 *password = malloc(n_read + 1); 214 if (! *password) { 215/* Can use perror() here even on Windows, as malloc is in std C library */ 216 perror(progname); 217 exit(-(SASL_FAIL)); 218 } 219 220 memcpy(*password, buf, n_read); 221 (*password)[n_read] = '\0'; /* be nice... */ 222 *passlen = (unsigned int)n_read; 223} 224 225void exit_sasl(int result, const char *errstr) __attribute__((noreturn)); 226 227void 228exit_sasl(int result, const char *errstr) 229{ 230 /* APPLE: split condition to fix unused format string parameter */ 231 if (errstr) 232 (void)fprintf(stderr, "%s: %s: %s\n", 233 progname, 234 sasl_errstring(result, NULL, NULL), 235 errstr); 236 else 237 (void)fprintf(stderr, "%s: %s\n", 238 progname, 239 sasl_errstring(result, NULL, NULL)); 240 241 exit(result < 0 ? -result : result); 242} 243 244int good_getopt(void *context __attribute__((unused)), 245 const char *plugin_name __attribute__((unused)), 246 const char *option, 247 const char **result, 248 unsigned *len) 249{ 250 if (sasldb_path && !strcmp(option, "sasldb_path")) { 251 *result = sasldb_path; 252 if (len) 253 *len = (unsigned) strlen(sasldb_path); 254 return SASL_OK; 255 } 256 257 return SASL_FAIL; 258} 259 260static struct sasl_callback goodsasl_cb[] = { 261 { SASL_CB_GETOPT, (sasl_callback_ft)&good_getopt, NULL }, 262 { SASL_CB_LIST_END, NULL, NULL } 263}; 264 265int 266main(int argc, char *argv[]) 267{ 268 int flag_pipe = 0, flag_create = 0, flag_disable = 0, flag_error = 0; 269 int flag_nouserpass = 0; 270 int c; 271 char *userid; 272 char *password = NULL; 273 char *verify; 274 unsigned passlen = 0; 275 unsigned verifylen; 276 const char *errstr = NULL; 277 int result; 278 sasl_conn_t *conn; 279 char *user_domain = NULL; 280 char *appname = "saslpasswd"; 281 const char *sasl_implementation; 282 int libsasl_version; 283 int libsasl_major; 284 int libsasl_minor; 285 int libsasl_step; 286 287#ifdef WIN32 288 /* initialize winsock */ 289 WSADATA wsaData; 290 291 result = WSAStartup( MAKEWORD(2, 0), &wsaData ); 292 if ( result != 0) { 293 exit_sasl(SASL_FAIL, "WSAStartup"); 294 } 295#endif 296 297 memset(myhostname, 0, sizeof(myhostname)); 298 result = gethostname(myhostname, sizeof(myhostname)-1); 299 if (result == -1) exit_sasl(SASL_FAIL, "gethostname"); 300 301 if (! argv[0]) 302 progname = "saslpasswd"; 303 else { 304 progname = strrchr(argv[0], HIER_DELIMITER); 305 if (progname) 306 progname++; 307 else 308 progname = argv[0]; 309 } 310 311 while ((c = getopt(argc, argv, "vpcdnf:u:a:h?")) != EOF) 312 switch (c) { 313 case 'p': 314 flag_pipe = 1; 315 break; 316 case 'c': 317 if (flag_disable) 318 flag_error = 1; 319 else 320 flag_create = 1; 321 break; 322 case 'd': 323 if (flag_create) 324 flag_error = 1; 325 else 326 flag_disable = 1; 327 break; 328 case 'n': 329 flag_nouserpass = 1; 330 break; 331 case 'u': 332 user_domain = optarg; 333 break; 334 case 'f': 335 sasldb_path = optarg; 336 break; 337 case 'a': 338 appname = optarg; 339 if (strchr(optarg, '/') != NULL) { 340 (void)fprintf(stderr, "appname must not contain /\n"); 341 exit(-(SASL_FAIL)); 342 } 343 break; 344 case 'v': 345 sasl_version (&sasl_implementation, &libsasl_version); 346 libsasl_major = libsasl_version >> 24; 347 libsasl_minor = (libsasl_version >> 16) & 0xFF; 348 libsasl_step = libsasl_version & 0xFFFF; 349 350 (void)fprintf(stderr, "\nThis product includes software developed by Computing Services\n" 351 "at Carnegie Mellon University (http://www.cmu.edu/computing/).\n\n" 352 "Built against SASL API version %u.%u.%u\n" 353 "LibSasl version %u.%u.%u by \"%s\"\n", 354 SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP, 355 libsasl_major, libsasl_minor, libsasl_step, sasl_implementation); 356 exit(0); 357 break; 358 default: 359 flag_error = 1; 360 break; 361 } 362 363 if (optind != argc - 1) 364 flag_error = 1; 365 366 if (flag_error) { 367 (void)fprintf(stderr, 368 "\nThis product includes software developed by Computing Services\n" 369 "at Carnegie Mellon University (http://www.cmu.edu/computing/).\n\n" 370 "%s: usage: %s [-v] [-c [-p] [-n]] [-d] [-a appname] [-f sasldb] [-u DOM] userid\n" 371 "\t-p\tpipe mode -- no prompt, password read on stdin\n" 372 "\t-c\tcreate -- ask mechs to create the account\n" 373 "\t-d\tdisable -- ask mechs to disable/delete the account\n" 374 "\t-n\tno userPassword -- don't set plaintext userPassword property\n" 375 "\t \t (only set mechanism-specific secrets)\n" 376 "\t-f sasldb\tuse given file as sasldb\n" 377 "\t-a appname\tuse appname as application name\n" 378 "\t-u DOM\tuse DOM for user domain\n" 379 "\t-v\tprint version numbers and exit\n", 380 progname, progname); 381 exit(-(SASL_FAIL)); 382 } 383 384 userid = argv[optind]; 385 386 result = sasl_server_init(goodsasl_cb, appname); 387 if (result != SASL_OK) 388 exit_sasl(result, NULL); 389 390 result = sasl_server_new("sasldb", 391 myhostname, 392 user_domain, 393 NULL, 394 NULL, 395 NULL, 396 0, 397 &conn); 398 if (result != SASL_OK) 399 exit_sasl(result, NULL); 400 401#ifndef WIN32 402 if (! flag_pipe && ! isatty(STDIN_FILENO)) 403 flag_pipe = 1; 404#endif /*WIN32*/ 405 406 if (!flag_disable) { 407 read_password("Password: ", flag_pipe, &password, &passlen); 408 409 if (! flag_pipe) { 410 read_password("Again (for verification): ", flag_pipe, &verify, 411 &verifylen); 412 if (passlen != verifylen 413 || memcmp(password, verify, verifylen)) { 414 fprintf(stderr, "%s: passwords don't match; aborting\n", 415 progname); 416 exit(-(SASL_BADPARAM)); 417 } 418 } 419 } 420 421 result = sasl_setpass(conn, 422 userid, 423 password, 424 passlen, 425 NULL, 0, 426 (flag_create ? SASL_SET_CREATE : 0) 427 | (flag_disable ? SASL_SET_DISABLE : 0) 428 | (flag_nouserpass ? SASL_SET_NOPLAIN : 0)); 429 430 if (result != SASL_OK && !flag_disable) 431 exit_sasl(result, NULL); 432 else { 433 struct propctx *propctx = NULL; 434 const char *delete_request[] = { "cmusaslsecretCRAM-MD5", 435 "cmusaslsecretDIGEST-MD5", 436 "cmusaslsecretPLAIN", 437 NULL }; 438 int ret = SASL_OK; 439 /* Either we were setting and succeeded or we were disabling and 440 failed. In either case, we want to wipe old entries */ 441 442 /* Delete the possibly old entries */ 443 /* We don't care if these fail */ 444 propctx = prop_new(0); 445 if (!propctx) ret = SASL_FAIL; 446 if (!ret) ret = prop_request(propctx, delete_request); 447 if (!ret) { 448 ret = prop_set(propctx, "cmusaslsecretCRAM-MD5", NULL, 0); 449 ret = prop_set(propctx, "cmusaslsecretDIGEST-MD5", NULL, 0); 450 ret = prop_set(propctx, "cmusaslsecretPLAIN", NULL, 0); 451 ret = sasl_auxprop_store(conn, propctx, userid); 452 } 453 if (propctx) prop_dispose(&propctx); 454 } 455 456 if (result != SASL_OK) 457/* errstr is currently always NULL */ 458 exit_sasl(result, errstr); 459 460 sasl_dispose(&conn); 461 sasl_done(); 462 463 return 0; 464} 465