int /* O - 0 on success, -1 on error */ cupsDoAuthentication( http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ const char *method, /* I - Request method ("GET", "POST", "PUT") */ const char *resource) /* I - Resource path */ { const char *password, /* Password string */ *www_auth; /* WWW-Authenticate header */ char prompt[1024], /* Prompt for user */ realm[HTTP_MAX_VALUE], /* realm="xyz" string */ nonce[HTTP_MAX_VALUE]; /* nonce="xyz" string */ int localauth; /* Local authentication result */ _cups_globals_t *cg; /* Global data */ DEBUG_printf(("cupsDoAuthentication(http=%p, method=\"%s\", resource=\"%s\")", http, method, resource)); if (!http) http = _cupsConnect(); if (!http || !method || !resource) return (-1); DEBUG_printf(("2cupsDoAuthentication: digest_tries=%d, userpass=\"%s\"", http->digest_tries, http->userpass)); DEBUG_printf(("2cupsDoAuthentication: WWW-Authenticate=\"%s\"", httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE))); /* * Clear the current authentication string... */ httpSetAuthString(http, NULL, NULL); /* * See if we can do local authentication... */ if (http->digest_tries < 3) { if ((localauth = cups_local_auth(http)) == 0) { DEBUG_printf(("2cupsDoAuthentication: authstring=\"%s\"", http->authstring)); if (http->status == HTTP_STATUS_UNAUTHORIZED) http->digest_tries ++; return (0); } else if (localauth == -1) { http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED; return (-1); /* Error or canceled */ } } /* * Nope, see if we should retry the current username:password... */ www_auth = http->fields[HTTP_FIELD_WWW_AUTHENTICATE]; if ((http->digest_tries > 1 || !http->userpass[0]) && (!_cups_strncasecmp(www_auth, "Basic", 5) || !_cups_strncasecmp(www_auth, "Digest", 6))) { /* * Nope - get a new password from the user... */ char default_username[HTTP_MAX_VALUE]; /* Default username */ cg = _cupsGlobals(); if (!cg->lang_default) cg->lang_default = cupsLangDefault(); if (httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "username", default_username)) cupsSetUser(default_username); snprintf(prompt, sizeof(prompt), _cupsLangString(cg->lang_default, _("Password for %s on %s? ")), cupsUser(), http->hostname[0] == '/' ? "localhost" : http->hostname); http->digest_tries = _cups_strncasecmp(www_auth, "Digest", 6) != 0; http->userpass[0] = '\0'; if ((password = cupsGetPassword2(prompt, http, method, resource)) == NULL) { http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED; return (-1); } snprintf(http->userpass, sizeof(http->userpass), "%s:%s", cupsUser(), password); } else if (http->status == HTTP_STATUS_UNAUTHORIZED) http->digest_tries ++; if (http->status == HTTP_STATUS_UNAUTHORIZED && http->digest_tries >= 3) { DEBUG_printf(("1cupsDoAuthentication: Too many authentication tries (%d)", http->digest_tries)); http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED; return (-1); } /* * Got a password; encode it for the server... */ #ifdef HAVE_GSSAPI if (!_cups_strncasecmp(www_auth, "Negotiate", 9)) { /* * Kerberos authentication... */ if (_cupsSetNegotiateAuthString(http, method, resource)) { http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED; return (-1); } } else #endif /* HAVE_GSSAPI */ if (!_cups_strncasecmp(www_auth, "Basic", 5)) { /* * Basic authentication... */ char encode[256]; /* Base64 buffer */ httpEncode64_2(encode, sizeof(encode), http->userpass, (int)strlen(http->userpass)); httpSetAuthString(http, "Basic", encode); } else if (!_cups_strncasecmp(www_auth, "Digest", 6)) { /* * Digest authentication... */ char encode[33], /* MD5 buffer */ digest[1024]; /* Digest auth data */ httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "realm", realm); httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "nonce", nonce); httpMD5(cupsUser(), realm, strchr(http->userpass, ':') + 1, encode); httpMD5Final(nonce, method, resource, encode); snprintf(digest, sizeof(digest), "username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", " "response=\"%s\"", cupsUser(), realm, nonce, resource, encode); httpSetAuthString(http, "Digest", digest); } else { DEBUG_printf(("1cupsDoAuthentication: Unknown auth type: \"%s\"", www_auth)); http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED; return (-1); } DEBUG_printf(("1cupsDoAuthentication: authstring=\"%s\"", http->authstring)); return (0); }
int /* O - Exit status */ main(int argc, /* I - Number of command-line arguments */ char *argv[]) /* I - Command-line arguments */ { int i; /* Looping var */ char *opt; /* Option pointer */ const char *username; /* Pointer to username */ const char *groupname; /* Pointer to group name */ int op; /* Operation (add, change, delete) */ const char *passwd; /* Password string */ FILE *infile, /* Input file */ *outfile; /* Output file */ char line[256], /* Line from file */ userline[17], /* User from line */ groupline[17], /* Group from line */ md5line[33], /* MD5-sum from line */ md5new[33]; /* New MD5 sum */ char passwdmd5[1024], /* passwd.md5 file */ passwdold[1024], /* passwd.old file */ passwdnew[1024]; /* passwd.tmp file */ char *newpass, /* new password */ *oldpass; /* old password */ int flag; /* Password check flags... */ int fd; /* Password file descriptor */ int error; /* Write error */ _cups_globals_t *cg = _cupsGlobals(); /* Global data */ cups_lang_t *lang; /* Language info */ #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) struct sigaction action; /* Signal action */ #endif /* HAVE_SIGACTION && !HAVE_SIGSET*/ _cupsSetLocale(argv); lang = cupsLangDefault(); /* * Check to see if stdin, stdout, and stderr are still open... */ if (fcntl(0, F_GETFD, &i) || fcntl(1, F_GETFD, &i) || fcntl(2, F_GETFD, &i)) { /* * No, return exit status 2 and don't try to send any output since * someone is trying to bypass the security on the server. */ return (2); } /* * Find the server directory... */ snprintf(passwdmd5, sizeof(passwdmd5), "%s/passwd.md5", cg->cups_serverroot); snprintf(passwdold, sizeof(passwdold), "%s/passwd.old", cg->cups_serverroot); snprintf(passwdnew, sizeof(passwdnew), "%s/passwd.new", cg->cups_serverroot); /* * Find the default system group... */ if (getgrnam(CUPS_DEFAULT_GROUP)) groupname = CUPS_DEFAULT_GROUP; else groupname = "unknown"; endgrent(); username = NULL; op = CHANGE; /* * Parse command-line options... */ for (i = 1; i < argc; i ++) if (argv[i][0] == '-') for (opt = argv[i] + 1; *opt; opt ++) switch (*opt) { case 'a' : /* Add */ op = ADD; break; case 'x' : /* Delete */ op = DELETE; break; case 'g' : /* Group */ i ++; if (i >= argc) usage(stderr); groupname = argv[i]; break; case 'h' : /* Help */ usage(stdout); break; default : /* Bad option */ usage(stderr); break; } else if (!username) username = argv[i]; else usage(stderr); /* * See if we are trying to add or delete a password when we aren't logged in * as root... */ if (getuid() && getuid() != geteuid() && (op != CHANGE || username)) { _cupsLangPuts(stderr, _("lppasswd: Only root can add or delete passwords!\n")); return (1); } /* * Fill in missing info... */ if (!username) username = cupsUser(); oldpass = newpass = NULL; /* * Obtain old and new password _before_ locking the database * to keep users from locking the file indefinitely. */ if (op == CHANGE && getuid()) { if ((passwd = cupsGetPassword(_("Enter old password:"******"lppasswd: Unable to copy password string: %s\n"), strerror(errno)); return (1); } } /* * Now get the new password, if necessary... */ if (op != DELETE) { if ((passwd = cupsGetPassword( _cupsLangString(lang, _("Enter password:"******"lppasswd: Unable to copy password string: %s\n"), strerror(errno)); return (1); } if ((passwd = cupsGetPassword( _cupsLangString(lang, _("Enter password again:")))) == NULL) return (1); if (strcmp(passwd, newpass) != 0) { _cupsLangPuts(stderr, _("lppasswd: Sorry, passwords don't match!\n")); return (1); } /* * Check that the password contains at least one letter and number. */ flag = 0; for (passwd = newpass; *passwd; passwd ++) if (isdigit(*passwd & 255)) flag |= 1; else if (isalpha(*passwd & 255)) flag |= 2; /* * Only allow passwords that are at least 6 chars, have a letter and * a number, and don't contain the username. */ if (strlen(newpass) < 6 || strstr(newpass, username) != NULL || flag != 3) { _cupsLangPuts(stderr, _("lppasswd: Sorry, password rejected.\n" "Your password must be at least 6 characters long, " "cannot contain\n" "your username, and must contain at least one letter " "and number.\n")); return (1); } } /* * Ignore SIGHUP, SIGINT, SIGTERM, and SIGXFSZ (if defined) for the * remainder of the time so that we won't end up with bogus password * files... */ #ifndef WIN32 # if defined(HAVE_SIGSET) sigset(SIGHUP, SIG_IGN); sigset(SIGINT, SIG_IGN); sigset(SIGTERM, SIG_IGN); # ifdef SIGXFSZ sigset(SIGXFSZ, SIG_IGN); # endif /* SIGXFSZ */ # elif defined(HAVE_SIGACTION) memset(&action, 0, sizeof(action)); action.sa_handler = SIG_IGN; sigaction(SIGHUP, &action, NULL); sigaction(SIGINT, &action, NULL); sigaction(SIGTERM, &action, NULL); # ifdef SIGXFSZ sigaction(SIGXFSZ, &action, NULL); # endif /* SIGXFSZ */ # else signal(SIGHUP, SIG_IGN); signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); # ifdef SIGXFSZ signal(SIGXFSZ, SIG_IGN); # endif /* SIGXFSZ */ # endif #endif /* !WIN32 */ /* * Open the output file. */ if ((fd = open(passwdnew, O_WRONLY | O_CREAT | O_EXCL, 0400)) < 0) { if (errno == EEXIST) _cupsLangPuts(stderr, _("lppasswd: Password file busy!\n")); else _cupsLangPrintf(stderr, _("lppasswd: Unable to open password file: %s\n"), strerror(errno)); return (1); } if ((outfile = fdopen(fd, "w")) == NULL) { _cupsLangPrintf(stderr, _("lppasswd: Unable to open password file: %s\n"), strerror(errno)); unlink(passwdnew); return (1); } setbuf(outfile, NULL); /* * Open the existing password file and create a new one... */ infile = fopen(passwdmd5, "r"); if (infile == NULL && errno != ENOENT && op != ADD) { _cupsLangPrintf(stderr, _("lppasswd: Unable to open password file: %s\n"), strerror(errno)); fclose(outfile); unlink(passwdnew); return (1); } /* * Read lines from the password file; the format is: * * username:group:MD5-sum */ error = 0; userline[0] = '\0'; groupline[0] = '\0'; md5line[0] = '\0'; if (infile) { while (fgets(line, sizeof(line), infile) != NULL) { if (sscanf(line, "%16[^:]:%16[^:]:%32s", userline, groupline, md5line) != 3) continue; if (strcmp(username, userline) == 0 && strcmp(groupname, groupline) == 0) break; if (fputs(line, outfile) == EOF) { _cupsLangPrintf(stderr, _("lppasswd: Unable to write to password file: %s\n"), strerror(errno)); error = 1; break; } } if (!error) { while (fgets(line, sizeof(line), infile) != NULL) if (fputs(line, outfile) == EOF) { _cupsLangPrintf(stderr, _("lppasswd: Unable to write to password file: %s\n"), strerror(errno)); error = 1; break; } } } if (op == CHANGE && (strcmp(username, userline) || strcmp(groupname, groupline))) { _cupsLangPrintf(stderr, _("lppasswd: user \"%s\" and group \"%s\" do not exist.\n"), username, groupname); error = 1; } else if (op != DELETE) { if (oldpass && strcmp(httpMD5(username, "CUPS", oldpass, md5new), md5line) != 0) { _cupsLangPuts(stderr, _("lppasswd: Sorry, password doesn't match!\n")); error = 1; } else { snprintf(line, sizeof(line), "%s:%s:%s\n", username, groupname, httpMD5(username, "CUPS", newpass, md5new)); if (fputs(line, outfile) == EOF) { _cupsLangPrintf(stderr, _("lppasswd: Unable to write to password file: %s\n"), strerror(errno)); error = 1; } } } /* * Close the files... */ if (infile) fclose(infile); if (fclose(outfile) == EOF) error = 1; /* * Error out gracefully as needed... */ if (error) { _cupsLangPuts(stderr, _("lppasswd: Password file not updated!\n")); unlink(passwdnew); return (1); } /* * Save old passwd file */ unlink(passwdold); if (link(passwdmd5, passwdold) && errno != ENOENT) { _cupsLangPrintf(stderr, _("lppasswd: failed to backup old password file: %s\n"), strerror(errno)); unlink(passwdnew); return (1); } /* * Install new password file */ if (rename(passwdnew, passwdmd5) < 0) { _cupsLangPrintf(stderr, _("lppasswd: failed to rename password file: %s\n"), strerror(errno)); unlink(passwdnew); return (1); } return (0); }