Example #1
0
struct backend *backend_connect(struct backend *ret_backend, const char *server,
				struct protocol_t *prot, const char *userid,
				sasl_callback_t *cb, const char **auth_status)
{
    /* need to (re)establish connection to server or create one */
    int sock = -1;
    int r;
    int err = -1;
    int ask = 1; /* should we explicitly ask for capabilities? */
    struct addrinfo hints, *res0 = NULL, *res;
    struct sockaddr_un sunsock;
    char buf[2048];
    struct sigaction action;
    struct backend *ret;
    char rsessionid[MAX_SESSIONID_SIZE];

    if (!ret_backend) {
	ret = xzmalloc(sizeof(struct backend));
	strlcpy(ret->hostname, server, sizeof(ret->hostname));
	ret->timeout = NULL;
    }
    else
	ret = ret_backend;

    if (server[0] == '/') { /* unix socket */
	res0 = &hints;
	memset(res0, 0, sizeof(struct addrinfo));
	res0->ai_family = PF_UNIX;
	res0->ai_socktype = SOCK_STREAM;

 	res0->ai_addr = (struct sockaddr *) &sunsock;
 	res0->ai_addrlen = sizeof(sunsock.sun_family) + strlen(server) + 1;
#ifdef SIN6_LEN
 	res0->ai_addrlen += sizeof(sunsock.sun_len);
 	sunsock.sun_len = res0->ai_addrlen;
#endif
	sunsock.sun_family = AF_UNIX;
	strlcpy(sunsock.sun_path, server, sizeof(sunsock.sun_path));

	/* XXX set that we are preauthed */

	/* change hostname to 'config_servername' */
	strlcpy(ret->hostname, config_servername, sizeof(ret->hostname));
    }
    else { /* inet socket */
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = PF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	err = getaddrinfo(server, prot->service, &hints, &res0);
	if (err) {
	    syslog(LOG_ERR, "getaddrinfo(%s) failed: %s",
		   server, gai_strerror(err));
	    goto error;
	}
    }

    /* Setup timeout */
    timedout = 0;
    action.sa_flags = 0;
    action.sa_handler = timed_out;
    sigemptyset(&action.sa_mask);
    if(sigaction(SIGALRM, &action, NULL) < 0) 
    {
	syslog(LOG_ERR, "Setting timeout in backend_connect failed: sigaction: %m");
	/* continue anyway */
    }
    
    for (res = res0; res; res = res->ai_next) {
	sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
	if (sock < 0)
	    continue;
	alarm(config_getint(IMAPOPT_CLIENT_TIMEOUT));
	if (connect(sock, res->ai_addr, res->ai_addrlen) >= 0)
	    break;
	if(errno == EINTR && timedout == 1)
	    errno = ETIMEDOUT;
	close(sock);
	sock = -1;
    }

    /* Remove timeout code */
    alarm(0);
    signal(SIGALRM, SIG_IGN);
    
    if (sock < 0) {
	if (res0 != &hints)
	    freeaddrinfo(res0);
	syslog(LOG_ERR, "connect(%s) failed: %m", server);
	goto error;
    }
    memcpy(&ret->addr, res->ai_addr, res->ai_addrlen);
    if (res0 != &hints)
	freeaddrinfo(res0);

    ret->in = prot_new(sock, 0);
    ret->out = prot_new(sock, 1);
    ret->sock = sock;
    prot_setflushonread(ret->in, ret->out);
    ret->prot = prot;

    /* use literal+ to send literals */
    prot_setisclient(ret->in, 1);
    prot_setisclient(ret->out, 1);
    
    if (prot->banner.auto_capa) {
	/* try to get the capabilities from the banner */
	r = ask_capability(ret, /*dobanner*/1, AUTO_CAPA_BANNER);
	if (r) {
	    /* found capabilities in banner -> don't ask */
	    ask = 0;
	}
    }
    else {
	do { /* read the initial greeting */
	    if (!prot_fgets(buf, sizeof(buf), ret->in)) {
		syslog(LOG_ERR,
		       "backend_connect(): couldn't read initial greeting: %s",
		       ret->in->error ? ret->in->error : "(null)");
		goto error;
	    }
	} while (strncasecmp(buf, prot->banner.resp,
			     strlen(prot->banner.resp)));
	strncpy(ret->banner, buf, 2048);
    }

    if (ask) {
	/* get the capabilities */
	ask_capability(ret, /*dobanner*/0, AUTO_CAPA_NO);
    }

    /* now need to authenticate to backend server,
       unless we're doing LMTP/CSYNC on a UNIX socket (deliver/sync_client) */
    if ((server[0] != '/') ||
	(strcmp(prot->sasl_service, "lmtp") &&
	 strcmp(prot->sasl_service, "csync"))) {
	char *old_mechlist = backend_get_cap_params(ret, CAPA_AUTH);
	const char *my_status;

	if ((r = backend_authenticate(ret, userid, cb, &my_status))) {
	    syslog(LOG_ERR, "couldn't authenticate to backend server: %s",
		   sasl_errstring(r, NULL, NULL));
	    free(old_mechlist);
	    goto error;
	}
	else {
	    const void *ssf;

	    sasl_getprop(ret->saslconn, SASL_SSF, &ssf);
	    if (*((sasl_ssf_t *) ssf)) {
		/* if we have a SASL security layer, compare SASL mech lists
		   before/after AUTH to check for a MITM attack */
		char *new_mechlist;
		int auto_capa = (prot->sasl_cmd.auto_capa == AUTO_CAPA_AUTH_SSF);

		if (!strcmp(prot->service, "sieve")) {
		    /* XXX  Hack to handle ManageSieve servers.
		     * No way to tell from protocol if server will
		     * automatically send capabilities, so we treat it
		     * as optional.
		     */
		    char ch;

		    /* wait and probe for possible auto-capability response */
		    usleep(250000);
		    prot_NONBLOCK(ret->in);
		    if ((ch = prot_getc(ret->in)) != EOF) {
			prot_ungetc(ch, ret->in);
		    } else {
			auto_capa = AUTO_CAPA_AUTH_NO;
		    }
		    prot_BLOCK(ret->in);
		}

		ask_capability(ret, /*dobanner*/0, auto_capa);
		new_mechlist = backend_get_cap_params(ret, CAPA_AUTH);
		if (new_mechlist &&
		    old_mechlist &&
		    strcmp(new_mechlist, old_mechlist)) {
		    syslog(LOG_ERR, "possible MITM attack:"
			   "list of available SASL mechanisms changed");
		    free(new_mechlist);
		    free(old_mechlist);
		    goto error;
		}
		free(new_mechlist);
	    }
	    else if (prot->sasl_cmd.auto_capa == AUTO_CAPA_AUTH_OK) {
		/* try to get the capabilities from the AUTH success response */
		forget_capabilities(ret);
		parse_capability(ret, my_status);
		post_parse_capability(ret);
	    }

	    if (!(strcmp(prot->service, "imap") &&
		 (strcmp(prot->service, "pop3")))) {
		parse_sessionid(my_status, rsessionid);
		syslog(LOG_NOTICE, "proxy %s sessionid=<%s> remote=<%s>", userid, session_id(), rsessionid);
	    }
	}

	if (auth_status) *auth_status = my_status;
	free(old_mechlist);
    }

    /* start compression if requested and both client/server support it */
    if (config_getswitch(IMAPOPT_PROXY_COMPRESS) && ret &&
	CAPA(ret, CAPA_COMPRESS) &&
	prot->compress_cmd.cmd &&
	do_compress(ret, &prot->compress_cmd)) {

	syslog(LOG_ERR, "couldn't enable compression on backend server");
	goto error;
    }

    return ret;

error:
    forget_capabilities(ret);
    if (ret->in) {
	prot_free(ret->in);
	ret->in = NULL;
    }
    if (ret->out) {
	prot_free(ret->out);
	ret->out = NULL;
    }
    if (sock >= 0)
	close(sock);
    if (ret->saslconn) {
	sasl_dispose(&ret->saslconn);
	ret->saslconn = NULL;
    }
    if (!ret_backend)
	free(ret);
    return NULL;
}
/* public: authenticate user */
PAM_VISIBLE int
pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
	modopt_t *options = NULL;
	const char *user, *password, *rhost;
	int rc;
	PGresult *res;
	PGconn *conn;	

	user = NULL; password = NULL; rhost = NULL;

	if ((rc = pam_get_item(pamh, PAM_RHOST, (const void **)&rhost)) == PAM_SUCCESS) {

		if ((rc = pam_get_user(pamh, &user, NULL)) == PAM_SUCCESS) {

			if ((options = mod_options(argc, argv)) != NULL) {

				DBGLOG("attempting to authenticate: %s, %s", user, options->query_auth);
				if ((rc = pam_get_pass(pamh, PAM_AUTHTOK, &password, PASSWORD_PROMPT, options->std_flags)) == PAM_SUCCESS) {

					if ((rc = backend_authenticate(pam_get_service(pamh), user, password, rhost, options)) == PAM_SUCCESS) {
						if ((password == 0 || *password == 0) && (flags & PAM_DISALLOW_NULL_AUTHTOK)) {
							rc = PAM_AUTH_ERR; 
						} else {
							SYSLOG("(%s) user %s authenticated.", pam_get_service(pamh), user);
						}

					} else {

                        char* rhost = NULL;
                        if (pam_get_item(pamh, PAM_RHOST, (void *) &rhost) == PAM_SUCCESS) {
                            SYSLOG("couldn't authenticate user %s (%s)", user, rhost);
                        } else {
                            SYSLOG("couldn't authenticate user %s", user);
                        }

					}

				} else {

					SYSLOG("couldn't get pass");

				}
			}
		}
	}
	
	if (rc == PAM_SUCCESS) {
		if (options->query_auth_succ) {
			if ((conn = db_connect(options))) {
				pg_execParam(conn, &res, options->query_auth_succ, pam_get_service(pamh), user, password, rhost);
				PQclear(res);
				PQfinish(conn);
			}
		}
	} else {
		if (options->query_auth_fail) {
			if ((conn = db_connect(options))) {
				pg_execParam(conn, &res, options->query_auth_fail, pam_get_service(pamh), user, password, rhost);
				PQclear(res);
				PQfinish(conn);
			}
		}
	}

	//free_mod_options(options);
	return rc;
}
Example #3
0
static int backend_login(struct backend *ret, const char *userid,
			 sasl_callback_t *cb, const char **auth_status,
			 int noauth)
{
    int r = 0;
    int ask = 1; /* should we explicitly ask for capabilities? */
    char buf[2048];
    struct protocol_t *prot = ret->prot;

    if (prot->type != TYPE_STD) return -1;

    if (prot->u.std.banner.auto_capa) {
	/* try to get the capabilities from the banner */
	r = ask_capability(ret, /*dobanner*/1, AUTO_CAPA_BANNER);
	if (r) {
	    /* found capabilities in banner -> don't ask */
	    ask = 0;
	}
    }
    else {
	do { /* read the initial greeting */
	    if (!prot_fgets(buf, sizeof(buf), ret->in)) {
		syslog(LOG_ERR,
		       "backend_login(): couldn't read initial greeting: %s",
		       ret->in->error ? ret->in->error : "(null)");
		return -1;
	    }
	} while (strncasecmp(buf, prot->u.std.banner.resp,
			     strlen(prot->u.std.banner.resp)));
	xstrncpy(ret->banner, buf, 2048);
    }

    if (ask) {
	/* get the capabilities */
	ask_capability(ret, /*dobanner*/0, AUTO_CAPA_NO);
    }

    /* now need to authenticate to backend server,
       unless we're doing LMTP/CSYNC on a UNIX socket (deliver/sync_client) */
    if (!noauth) {
	char *old_mechlist = backend_get_cap_params(ret, CAPA_AUTH);
	const char *my_status;

	if ((r = backend_authenticate(ret, userid, cb, &my_status))) {
	    syslog(LOG_ERR, "couldn't authenticate to backend server: %s",
		   sasl_errstring(r, NULL, NULL));
	    free(old_mechlist);
	    return -1;
	}
	else {
	    const void *ssf;

	    sasl_getprop(ret->saslconn, SASL_SSF, &ssf);
	    if (*((sasl_ssf_t *) ssf)) {
		/* if we have a SASL security layer, compare SASL mech lists
		   before/after AUTH to check for a MITM attack */
		char *new_mechlist;
		int auto_capa = (prot->u.std.sasl_cmd.auto_capa == AUTO_CAPA_AUTH_SSF);

		if (!strcmp(prot->service, "sieve")) {
		    /* XXX  Hack to handle ManageSieve servers.
		     * No way to tell from protocol if server will
		     * automatically send capabilities, so we treat it
		     * as optional.
		     */
		    char ch;

		    /* wait and probe for possible auto-capability response */
		    usleep(250000);
		    prot_NONBLOCK(ret->in);
		    if ((ch = prot_getc(ret->in)) != EOF) {
			prot_ungetc(ch, ret->in);
		    } else {
			auto_capa = AUTO_CAPA_AUTH_NO;
		    }
		    prot_BLOCK(ret->in);
		}

		ask_capability(ret, /*dobanner*/0, auto_capa);
		new_mechlist = backend_get_cap_params(ret, CAPA_AUTH);
		if (new_mechlist &&
		    old_mechlist &&
		    strcmp(new_mechlist, old_mechlist)) {
		    syslog(LOG_ERR, "possible MITM attack:"
			   "list of available SASL mechanisms changed");

		    if (new_mechlist) free(new_mechlist);
		    if (old_mechlist) free(old_mechlist);
		    return -1;
		}
		free(new_mechlist);
	    }
	    else if (prot->u.std.sasl_cmd.auto_capa == AUTO_CAPA_AUTH_OK) {
		/* try to get the capabilities from the AUTH success response */
		forget_capabilities(ret);
		parse_capability(ret, my_status);
		post_parse_capability(ret);
	    }

	    if (!(strcmp(prot->service, "imap") &&
		 (strcmp(prot->service, "pop3")))) {
		char rsessionid[MAX_SESSIONID_SIZE];
		parse_sessionid(my_status, rsessionid);
		syslog(LOG_NOTICE, "auditlog: proxy %s sessionid=<%s> remote=<%s>", userid, session_id(), rsessionid);
	    }
	}

	if (auth_status) *auth_status = my_status;
	free(old_mechlist);
    }

    /* start compression if requested and both client/server support it */
    if (config_getswitch(IMAPOPT_PROXY_COMPRESS) &&
	CAPA(ret, CAPA_COMPRESS) &&
	prot->u.std.compress_cmd.cmd) {
	r = do_compress(ret, &prot->u.std.compress_cmd);
	if (r) {
	    syslog(LOG_NOTICE, "couldn't enable compression on backend server: %s", error_message(r));
	    r = 0; /* not a fail-level error */
	}
    }

    return 0;
}
/* public: change password */
PAM_VISIBLE int
pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
	modopt_t *options = NULL;
	int rc;
	const char *user, *pass, *newpass, *rhost;
	const void *oldtok;
	char *newpass_crypt;
	PGconn *conn;
	PGresult *res;

	user = NULL; pass = NULL; newpass = NULL; rhost = NULL; newpass_crypt = NULL;

	if ((options = mod_options(argc, argv)) != NULL) {
		if ((rc = pam_get_item(pamh, PAM_RHOST, (const void **)&rhost)) == PAM_SUCCESS) 
			rc = pam_get_user(pamh, &user, NULL);
	} else
		rc = 1;

	if ((rc == PAM_SUCCESS) && (flags & PAM_PRELIM_CHECK)) {
		if (getuid() != 0) {
			if ((rc = pam_get_pass(pamh, PAM_OLDAUTHTOK, &pass, PASSWORD_PROMPT, options->std_flags)) == PAM_SUCCESS) {
				rc = backend_authenticate(pam_get_service(pamh), user, pass, rhost, options);
			} else {
				SYSLOG("could not retrieve password from '%s'", user);
			}
		} else {
			rc = PAM_SUCCESS;
		}
	} else if ((rc == PAM_SUCCESS) && (flags & PAM_UPDATE_AUTHTOK)) {

		/* only try to check old password if user is not root */
		pass = newpass = NULL;
		if (getuid() != 0) {

			if ((rc = pam_get_item(pamh, PAM_OLDAUTHTOK, &oldtok)) == PAM_SUCCESS) {
				pass = (const char*) oldtok;
				if ((rc = backend_authenticate(pam_get_service(pamh), user, pass, rhost, options)) != PAM_SUCCESS) {
					SYSLOG("(%s) user '%s' not authenticated.", pam_get_service(pamh), user);
				}
			} else {
				SYSLOG("could not retrieve old token");
			}

		} else {
			rc = PAM_SUCCESS;
		}

		if (rc == PAM_SUCCESS) {

			if ((rc = pam_get_confirm_pass(pamh, &newpass, PASSWORD_PROMPT_NEW, PASSWORD_PROMPT_CONFIRM, options->std_flags)) == PAM_SUCCESS) {
				if((newpass_crypt = password_encrypt(options, user, newpass, NULL))) {
					if(!(conn = db_connect(options))) {
						rc = PAM_AUTHINFO_UNAVAIL;
					}
					if (rc == PAM_SUCCESS) {
						DBGLOG("query: %s", options->query_pwd);
						if(pg_execParam(conn, &res, options->query_pwd, pam_get_service(pamh), user, newpass_crypt, rhost) != PAM_SUCCESS) {
							rc = PAM_AUTH_ERR;
						} else {
							SYSLOG("(%s) password for '%s' was changed.", pam_get_service(pamh), user);
							PQclear(res);
						}
						PQfinish(conn);
					}
					free (newpass_crypt);
				} else {
					rc = PAM_BUF_ERR;
				}
			} else {
				SYSLOG("could not retrieve new authentication tokens");
			}
		}
	}
	//free_module_options(options);
	if (flags & (PAM_PRELIM_CHECK | PAM_UPDATE_AUTHTOK))
		return rc;
	else
		return PAM_AUTH_ERR;

}