📄 rpasswdd.c
字号:
struct pam_response **response, void *appdata_ptr){ int count=0; struct pam_response *reply;#ifdef USE_GNUTLS gnutls_session ssl = appdata_ptr;#else SSL *ssl = appdata_ptr;#endif if (num_msg <= 0) return PAM_CONV_ERR; D(("allocating empty response structure array.")); reply = (struct pam_response *) calloc(num_msg, sizeof(struct pam_response)); if (reply == NULL) { D(("no memory for responses")); return PAM_CONV_ERR; } D(("entering conversation function.")); for (count = 0; count < num_msg; ++count) { char *string = NULL; switch (msgm[count]->msg_style) { case PAM_PROMPT_ECHO_OFF: D(("PAM_PROMPT_ECHO_OFF")); if (send_string (ssl, PROMPT_ECHO_OFF, msgm[count]->msg) != 0) goto failed_conversation; if (read_string (ssl, &string) != PAM_SUCCESS) goto failed_conversation; break; case PAM_PROMPT_ECHO_ON: D(("PAM_PROMPT_ECHO_ON")); if (send_string (ssl, PROMPT_ECHO_OFF, msgm[count]->msg) != 0) goto failed_conversation; if (read_string (ssl, &string) != PAM_SUCCESS) goto failed_conversation; break; case PAM_ERROR_MSG: D(("PAM_ERROR_MSG")); if (send_string (ssl, ERROR_MSG, msgm[count]->msg) != 0) goto failed_conversation; break; case PAM_TEXT_INFO: D(("PAM_TEXT_INFO")); if (send_string (ssl, TEXT_INFO, msgm[count]->msg) != 0) goto failed_conversation; break; default: /* send_string (ssl, TEXT_INFO, _("erroneous conversation (%d)") ,msgm[count]->msg_style); */ goto failed_conversation; } if (string) { /* must add to reply array */ /* add string to list of responses */ reply[count].resp_retcode = 0; reply[count].resp = string; string = NULL; } } /* New (0.59+) behavior is to always have a reply - this is compatable with the X/Open (March 1997) spec. */ *response = reply; reply = NULL; return PAM_SUCCESS;failed_conversation: if (reply) { for (count=0; count<num_msg; ++count) { if (reply[count].resp == NULL) { continue; } switch (msgm[count]->msg_style) { case PAM_PROMPT_ECHO_ON: case PAM_PROMPT_ECHO_OFF: _pam_overwrite(reply[count].resp); free(reply[count].resp); break; case PAM_ERROR_MSG: case PAM_TEXT_INFO: /* should not actually be able to get here... */ free(reply[count].resp); } reply[count].resp = NULL; } /* forget reply too */ free(reply); reply = NULL; } return PAM_CONV_ERR;}/* Sanity check on locale string. Otherwise local lusers may be tempted to send us a locale of "../../../../../tmp" and deposit a message catalog there containing format strings with lots of %n's in them. */static intsane_locale (const char *name){ if (!name) return 0; if (strchr (name, '/')) return 0; if (strstr (name, "..")) return 0; /* Any other checks? */ return 1;}/* Handle new request. */static int#ifdef USE_GNUTLShandle_request (gnutls_session ssl, request_header *req, char *locale, const char *username, const char * program)#elsehandle_request (SSL *ssl, request_header *req, char *locale, const char *username, const char *program)#endif{ const struct pam_conv conv = { rpasswd_conv, ssl }; pam_handle_t *pamh = NULL; int flags = 0, ret; int retval = E_SUCCESS; int pw_buflen = 256; char *pw_buffer = alloca (pw_buflen); struct passwd pw_resultbuf; struct passwd *pw = NULL; if (debug_level > 0) dbg_log (_("handle_request: request received (Version = %d)"), req->version); if (req->version != RPASSWD_VERSION) { if (debug_level > 0) dbg_log (_("\cannot handle request version %d; current version is %d"), req->version, RPASSWD_VERSION); retval = E_WRONG_VERSION; goto send_finish; } if (locale && sane_locale(locale)) setlocale (LC_ALL, locale); /* Get password file entry... */ while (getpwnam_r (username, &pw_resultbuf, pw_buffer, pw_buflen, &pw) != 0 && errno == ERANGE) { errno = 0; pw_buflen += 256; pw_buffer = alloca (pw_buflen); } if (pw == NULL && req->request != START_ADMIN) { dbg_log ("passwd entry for \"%s\" not found", username); /* Dummy authentication. So the user will not see that this account does not exist. */ ret = pam_start ("rpasswd", username, &conv, &pamh); if (ret != PAM_SUCCESS) { retval = E_PAM_ERROR; goto send_finish; } /* We are not interested in the return value, we always assume a failed. */ pam_authenticate (pamh, flags); pam_end (pamh, PAM_SUCCESS); /* after using PAM we have to reset openlog data */ openlog (program, LOG_CONS | LOG_ODELAY, LOG_DAEMON); sleep (getlogindefs_num ("FAIL_DELAY", 1)); send_string (ssl, ERROR_MSG, "Authentication failure"); retval = E_FAILURE; goto send_finish; } /* Do extra authentication if run in admin mode or the passwort for root should be changed. */ if (req->request == START_ADMIN || (pw && pw->pw_uid == 0)) { const char *account; if (req->request == START_ADMIN) account = "root"; else account = username; /* Do PAM authentification at first. */ ret = pam_start ("rpasswd", account, &conv, &pamh); if (ret != PAM_SUCCESS) { /* after using PAM we have to reset openlog data */ openlog (program, LOG_CONS | LOG_ODELAY, LOG_DAEMON); dbg_log ("Couldn't initialize PAM: %s", pam_strerror (pamh, ret)); retval = E_PAM_ERROR; goto send_finish; } else if (req->request == START_ADMIN) { /* print the message only in admin mode. */ char host[MAXHOSTNAMELEN+1]; char *cp; gethostname (host, sizeof (host)); asprintf (&cp, _("Please authenticate as %s on %s"), account, host); send_string (ssl, TEXT_INFO, cp); free (cp); } ret = pam_authenticate (pamh, flags); if (ret != PAM_SUCCESS) { pam_end (pamh, ret); /* after using PAM we have to reset openlog data */ openlog (program, LOG_CONS | LOG_ODELAY, LOG_DAEMON); dbg_log (_("User %s: %s"), account, pam_strerror (pamh, ret)); sleep (getlogindefs_num ("FAIL_DELAY", 1)); send_string (ssl, ERROR_MSG, pam_strerror (pamh, ret)); retval = E_FAILURE; goto send_finish; } pam_end (pamh, PAM_SUCCESS); /* after using PAM we have to reset openlog data */ openlog (program, LOG_CONS | LOG_ODELAY, LOG_DAEMON); if (req->request == START_ADMIN) { /* In Admin mode send info text what we are now doing. */ char *cp; if (pw == NULL) /* Now send error message for admin. */ { dbg_log ("passwd entry for \"%s\" not found", username); retval = E_UNKNOWN_USER; goto send_finish; } asprintf (&cp, _("\nNow enter the new password for %s"), username); send_string (ssl, TEXT_INFO, cp); free (cp); } } else /* (req->request != START_ADMIN) */ { /* Set the real uid to the one of the user for which we wish to change the password and let the effective and saved uid to be root. With this, most PAM modules thinks they are called from a setuid root passwd program. Not needed if we run in Admin mode. In this case, PAM moduls should think passwd is called by root. */ if (setresuid (pw->pw_uid, 0, 0) == -1) { char *cp; if (asprintf (&cp, _("setresuid failed on server: %s"), strerror (errno)) > 0) { dbg_log (cp); send_string (ssl, ERROR_MSG, cp); free (cp); } retval = E_FAILURE; goto send_finish; } } ret = pam_start ("rpasswd", username, &conv, &pamh); if (ret != PAM_SUCCESS) { /* after using PAM we have to reset openlog data */ openlog (program, LOG_CONS | LOG_ODELAY, LOG_DAEMON); dbg_log ("Couldn't initialize PAM: %s", pam_strerror (pamh, ret)); retval = E_PAM_ERROR; goto send_finish; } ret = pam_chauthtok (pamh, flags); if (ret != PAM_SUCCESS) { pam_end (pamh, ret); /* after using PAM we have to reset openlog data */ openlog (program, LOG_CONS | LOG_ODELAY, LOG_DAEMON); dbg_log (_("User %s: %s"), username, pam_strerror (pamh, ret)); sleep (getlogindefs_num ("FAIL_DELAY", 1)); send_string (ssl, ERROR_MSG, pam_strerror (pamh, ret)); retval = E_FAILURE; goto send_finish; } pam_end (pamh, PAM_SUCCESS); /* after using PAM we have to reset openlog data */ openlog (program, LOG_CONS | LOG_ODELAY, LOG_DAEMON); if (retval) send_string (ssl, TEXT_INFO, _("Password not changed"));#if 0 else send_string (ssl, TEXT_INFO, _("Password changed"));#endif send_finish: { response_header resp; resp.type = FINISH; resp.data_len = 1;#ifdef USE_GNUTLS gnutls_record_send (ssl, &resp, sizeof (resp)); gnutls_record_send (ssl, &retval, resp.data_len);#else SSL_write (ssl, &resp, sizeof (resp)); SSL_write (ssl, &retval, resp.data_len);#endif } if (debug_level > 0) dbg_log (_("handle_request: exit (%d)"), retval); return retval;}#ifdef USE_GNUTLS/* These are global */static gnutls_certificate_credentials x509_cred;static gnutls_sessioninitialize_gnutls_session (const char *certificate, const char *privatekey){ gnutls_session session; gnutls_certificate_allocate_credentials (&x509_cred);#if 0 /* XXX */ gnutls_certificate_set_x509_trust_file (x509_cred, certificate, GNUTLS_X509_FMT_PEM); gnutls_certificate_set_x509_crl_file (x509_cred, CRLFILE, GNUTLS_X509_FMT_PEM);#endif gnutls_certificate_set_x509_key_file (x509_cred, certificate, privatekey, GNUTLS_X509_FMT_PEM); gnutls_init (&session, GNUTLS_SERVER); gnutls_set_default_priority (session); gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred); /* XXX gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST); gnutls_dh_set_prime_bits (session, DH_BITS); generate_dh_params(); gnutls_certificate_set_dh_params( x509_cred, dh_params); */ return session;}#endif/* This is the main loop. It can be replicated in different threads but the `poll' call makes sure only one thread handles an incoming connection. */static void#ifdef USE_GNUTLSserver_run (const char *certificate, const char *privatekey, const char *program)#elseserver_run (const char *program)#endif{ int i; for (i = 0; i < pollfd_cnt; i++) pollfd_conn[i].events = POLLRDNORM; while (1) { int nr; nr = poll (pollfd_conn, pollfd_cnt, -1); if (nr < 0) { /* Don't print error messages if poll is only interupted by a signal. */ if (errno != EINTR) dbg_log ("poll() failed: %s", strerror (errno)); continue; } /* We have a new incoming connection. Look at which socket. */ for (i = 0; i < pollfd_cnt; i++) { if (pollfd_conn[i].revents & (POLLRDNORM|POLLERR|POLLHUP|POLLNVAL)) { /* Accept the connection. */ int ssl_err; long fd; request_header req; char buf[256]; char *locale, *username;#ifdef USE_GNUTLS gnutls_session ssl;#else SSL *ssl;#endif fd = accept (pollfd_conn[i].fd, NULL, NULL); if (fd < 0) { dbg_log (_("while accepting connection: %s"), strerror_r (errno, buf, sizeof (buf))); continue; } /* TCP connection is ready. Do server side SSL. */#ifdef USE_GNUTLS ssl = initialize_gnutls_session (certificate, privatekey); gnutls_transport_set_ptr (ssl, (gnutls_transport_ptr)fd); ssl_err = gnutls_handshake (ssl); if (ssl_err < 0) { close (fd); gnutls_deinit (ssl); dbg_log (_("Handshake has failed (%s)"), gnutls_strerror (ssl_err)); continue; } /* see the Getting peer's information example */ /* print_info(session); */ /* XXX print cipher info and check client certificate */ if ((ssl_err = gnutls_record_recv (ssl, &req, sizeof (req))) <= 0) { if (debug_level > 0) { if (ssl_err == 0) dbg_log (_("error while reading request: %s"), _("client has closed the GNUTLS connection")); else dbg_log (_("error while reading request: %s"), gnutls_strerror (ssl_err)); } gnutls_deinit (ssl); close (fd); continue; }#else ssl = SSL_new (ctx); if (ssl == NULL) { dbg_log (_("cannot enable SSL encryption")); close (fd); continue; } SSL_set_fd (ssl, fd); ssl_err = SSL_accept (ssl); if (ssl_err < 1) { dbg_log ("SSL_accept: %s", ERR_error_string (ssl_err, NULL)); close (fd); continue; } /* Get the cipher - opt */ if (debug_level > 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -