From patchwork Tue Jul 24 16:34:53 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Selva Nair X-Patchwork-Id: 421 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director11.mail.ord1d.rsapps.net ([172.30.191.6]) by backend30.mail.ord1d.rsapps.net (Dovecot) with LMTP id 04cgBTTiV1sibgAAIUCqbw for ; Tue, 24 Jul 2018 22:36:36 -0400 Received: from proxy11.mail.ord1d.rsapps.net ([172.30.191.6]) by director11.mail.ord1d.rsapps.net (Dovecot) with LMTP id I1gKBTTiV1vzEgAAvGGmqA ; Tue, 24 Jul 2018 22:36:36 -0400 Received: from smtp28.gate.ord1d ([172.30.191.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy11.mail.ord1d.rsapps.net with LMTP id wIqcBDTiV1t6dAAAgKDEHA ; Tue, 24 Jul 2018 22:36:36 -0400 X-Spam-Threshold: 95 X-Spam-Score: 0 X-Spam-Flag: NO X-Virus-Scanned: OK X-Orig-To: openvpnslackdevel@openvpn.net X-Originating-Ip: [216.105.38.7] Authentication-Results: smtp28.gate.ord1d.rsapps.net; iprev=pass policy.iprev="216.105.38.7"; spf=pass smtp.mailfrom="openvpn-devel-bounces@lists.sourceforge.net" smtp.helo="lists.sourceforge.net"; dkim=fail (signature verification failed) header.d=sourceforge.net; dkim=fail (signature verification failed) header.d=sf.net; dkim=fail (signature verification failed) header.d=gmail.com; dmarc=fail (p=none; dis=none) header.from=gmail.com X-Suspicious-Flag: YES X-Classification-ID: 8c45bd16-8fb3-11e8-8001-525400ea129b-1-1 Received: from [216.105.38.7] ([216.105.38.7:37954] helo=lists.sourceforge.net) by smtp28.gate.ord1d.rsapps.net (envelope-from ) (ecelerity 4.2.1.56364 r(Core:4.2.1.14)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id 41/98-31335-332E75B5; Tue, 24 Jul 2018 22:36:35 -0400 Received: from [127.0.0.1] (helo=sfs-ml-2.v29.lw.sourceforge.com) by sfs-ml-2.v29.lw.sourceforge.com with esmtp (Exim 4.90_1) (envelope-from ) id 1fi9e3-0000e1-FF; Wed, 25 Jul 2018 02:35:19 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-2.v29.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.90_1) (envelope-from ) id 1fi9e2-0000dv-Nt for openvpn-devel@lists.sourceforge.net; Wed, 25 Jul 2018 02:35:18 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=References:In-Reply-To:Message-Id:Date:Subject:Cc: To:From:Sender:Reply-To:MIME-Version:Content-Type:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=09CtFrwt79qJAP2VQg0EkEA6GbkPlNHg1Efv76em1+4=; b=jGAONXJaeinpafbAXbJp2/oJFp QJUcxQcodiNjZsI9oFIKMF+txefnBJxnf76MoL3N9cUzk4B+SIkV3SAnEaC7WkeycmuAyJDQ2vaNe MEHLVXYRlvEFBkRhWTcMK3stuDvGsMS300Rohx3KObXPni+nlPOOs3Mo5d8mDG5PUnoE=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To :MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=09CtFrwt79qJAP2VQg0EkEA6GbkPlNHg1Efv76em1+4=; b=JHingg1eJQU+CVt6DtPjOMN2/5 WC+UmnPLv7jDEflVpKbtYt4mqjZ/IkFFkXZNpzP2XLFmhWcr9CWJ8TZm2S/rhM9vbQKKLD6laKIqm 1Mlp+0s2RKwEsFWhl7gTYRwQqGpX2dWRINv16NJEzYEbH9Zb2pAQdqob36JgzOjwjtgQ=; Received: from mail-io0-f181.google.com ([209.85.223.181]) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.90_1) id 1fi9e0-00HFwV-Py for openvpn-devel@lists.sourceforge.net; Wed, 25 Jul 2018 02:35:18 +0000 Received: by mail-io0-f181.google.com with SMTP id l14-v6so5109025iob.7 for ; Tue, 24 Jul 2018 19:35:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=09CtFrwt79qJAP2VQg0EkEA6GbkPlNHg1Efv76em1+4=; b=SayL0iabxsJtFhl9cy4jwhwcbAeaCGjIXAOQUN6iYuov4AoONdaD7L9MPms3P8wOpU act/DXPxYPs4VR/PtH3HoperYKMUPhvy7g47EC62qe4HSF6LJX2QCjaD2cs8DP+ynuoe izp+XIttyGzMjSglL64pg6d96QIM4Bed2W+tWCSZ5+wxa1b/k6JRie5Zf3+8FLPnQa7d GOkE3MGRJDfp14dOitTjfJwbw0RAFnj54mp5Nu9m+EUtlWMkaUlVmcRPwFOlBLMoxCDo Cq2Fddhz5zbnI9gAS499LXZ/w6jz8ddj6mUwgsge2/kBOJf2swFrZj+fVFQyUW8adRgQ WJdA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=09CtFrwt79qJAP2VQg0EkEA6GbkPlNHg1Efv76em1+4=; b=MOiGDluFQakoEbF2X5seWVEAfnhVQyNzvG/okWDB8vsBiXV3FyM8RhUb1MzY+Te09S WoXZtIlIE7n9x2LLC4niq+bFt//GgLWYY+T/MBGYiCZcNIU5T4o/CrFnbRBfYJNXV04i PgSq4xeJkwONuUmemXs1h/nfXRawfffqyvOHKGEdD32CX4KtID0x5GuJWEQlxMh9N2MJ uNaiJCUQr+RwbFRPKthyy0JpuMvUVa5Qhv3WUAmPagVm1P/2CPREOCLkQ0jrPKCsJGFU tBQ8n0C4YF9B3Il1HgGlWqMUiob8bQD1LOhaC2SHTkwr6ob0/hO7hLBckRVG+4mj532J Z9xA== X-Gm-Message-State: AOUpUlHCUi6d6efQ+GWq13nk3aa8wqM4CcYReHeKZqkkVS2d6f8idlc1 s3fGJzc6d1Gldg/WEGWVAbPg/UJBHjg1RA== X-Google-Smtp-Source: AAOMgpdV3elUbtIKZ0epegKiL0bn7kCEjQ9pGV8YQxLiCCueUje8lulF++3UtWb0CqR/6cOJie3Ntw== X-Received: by 2002:a5e:c106:: with SMTP id v6-v6mr15333467iol.262.1532486110828; Tue, 24 Jul 2018 19:35:10 -0700 (PDT) Received: from saturn.home.sansel.ca (CPE40167ea0e1c2-CM788df74daaa0.cpe.net.cable.rogers.com. [99.228.215.92]) by smtp.gmail.com with ESMTPSA id a64-v6sm1689012itg.28.2018.07.24.19.35.09 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 24 Jul 2018 19:35:10 -0700 (PDT) From: selva.nair@gmail.com To: openvpn-devel@lists.sourceforge.net Date: Tue, 24 Jul 2018 22:34:53 -0400 Message-Id: <1532486093-24793-1-git-send-email-selva.nair@gmail.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1516833453-18991-1-git-send-email-selva.nair@gmail.com> References: <1516833453-18991-1-git-send-email-selva.nair@gmail.com> X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. 2.4 DNS_FROM_AHBL_RHSBL RBL: Envelope sender listed in dnsbl.ahbl.org [listed in gmail.com.rhsbl.ahbl.org. IN] [A] 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider (selva.nair[at]gmail.com) -0.0 RCVD_IN_MSPIKE_H2 RBL: Average reputation (+2) [209.85.223.181 listed in wl.mailspike.net] -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [209.85.223.181 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.0 T_FILL_THIS_FORM_SHORT Fill in a short form with personal information X-Headers-End: 1fi9e0-00HFwV-Py Subject: [Openvpn-devel] [PATCH v3] Parse static challenge response in auth-pam plugin X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox From: Selva Nair If static challenge is in use, the password passed to the plugin by openvpn is of the form "SCRV1:base64-pass:base64-response". Parse this string to separate it into password and response and use them to respond to queries in the pam conversation function. On the plugin parameters line the substitution keyword for the static challenge response is "OTP". For example, for pam config named "test" that prompts for "user", "password" and "pin", use plugin openvpn-auth-pam.so "test user USERNAME password PASSWORD pin OTP" Signed-off-by: Selva Nair Signed-off-by: Selva Nair <selva.nair@gmail.com>
Acked-By: Gert Doering --- v2: Depends on the base64 export patch v3: match password string with "SCRV1:" instead of "SCRV1" (pointed out by Joe Bell ) src/plugins/auth-pam/README.auth-pam | 15 +++++--- src/plugins/auth-pam/auth-pam.c | 75 +++++++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 6 deletions(-) diff --git a/src/plugins/auth-pam/README.auth-pam b/src/plugins/auth-pam/README.auth-pam index e123690..9081565 100644 --- a/src/plugins/auth-pam/README.auth-pam +++ b/src/plugins/auth-pam/README.auth-pam @@ -36,19 +36,20 @@ pairs to answer PAM module queries. For example: - plugin openvpn-auth-pam.so "login login USERNAME password PASSWORD" + plugin openvpn-auth-pam.so "login login USERNAME password PASSWORD pin OTP" tells auth-pam to (a) use the "login" PAM module, (b) answer a -"login" query with the username given by the OpenVPN client, and -(c) answer a "password" query with the password given by the -OpenVPN client. This provides flexibility in dealing with the different +"login" query with the username given by the OpenVPN client, +(c) answer a "password" query with the password, and (d) answer a +"pin" query with the OTP given by the OpenVPN client. +This provides flexibility in dealing with different types of query strings which different PAM modules might generate. For example, suppose you were using a PAM module called "test" which queried for "name" rather than "login": plugin openvpn-auth-pam.so "test name USERNAME password PASSWORD" -While "USERNAME" "COMMONNAME" and "PASSWORD" are special strings which substitute +While "USERNAME" "COMMONNAME" "PASSWORD" and "OTP" are special strings which substitute to client-supplied values, it is also possible to name literal values to use as PAM module query responses. For example, suppose that the login module queried for a third parameter, "domain" which @@ -61,6 +62,10 @@ the operation of this plugin: client-cert-not-required username-as-common-name + static-challenge + +Use of --static challenege is required to pass a pin (represented by "OTP" in +parameter substituion) or a second password. Run OpenVPN with --verb 7 or higher to get debugging output from this plugin, including the list of queries presented by the diff --git a/src/plugins/auth-pam/auth-pam.c b/src/plugins/auth-pam/auth-pam.c index 26b0eeb..e22ce5f 100644 --- a/src/plugins/auth-pam/auth-pam.c +++ b/src/plugins/auth-pam/auth-pam.c @@ -6,6 +6,7 @@ * packet compression. * * Copyright (C) 2002-2018 OpenVPN Inc + * Copyright (C) 2016-2018 Selva Nair * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -64,6 +65,7 @@ /* Pointers to functions exported from openvpn */ static plugin_secure_memzero_t plugin_secure_memzero = NULL; +static plugin_base64_decode_t plugin_base64_decode = NULL; /* * Plugin state, used by foreground @@ -87,6 +89,7 @@ struct auth_pam_context * "USERNAME" -- substitute client-supplied username * "PASSWORD" -- substitute client-specified password * "COMMONNAME" -- substitute client certificate common name + * "OTP" -- substitute static challenge response if available */ #define N_NAME_VALUE 16 @@ -111,6 +114,7 @@ struct user_pass { char username[128]; char password[128]; char common_name[128]; + char response[128]; const struct name_value_list *name_value_list; }; @@ -276,6 +280,66 @@ name_value_match(const char *query, const char *match) return strncasecmp(match, query, strlen(match)) == 0; } +/* + * Split and decode up->password in the form SCRV1:base64_pass:base64_response + * into pass and response and save in up->password and up->response. + * If the password is not in the expected format, input is not changed. + */ +static void +split_scrv1_password(struct user_pass *up) +{ + const int skip = strlen("SCRV1:"); + if (strncmp(up->password, "SCRV1:", skip) != 0) + { + return; + } + + char *tmp = strdup(up->password); + if (!tmp) + { + fprintf(stderr, "AUTH-PAM: out of memory parsing static challenge password\n"); + goto out; + } + + char *pass = tmp + skip; + char *resp = strchr(pass, ':'); + if (!resp) /* string not in SCRV1:xx:yy format */ + { + goto out; + } + *resp++ = '\0'; + + int n = plugin_base64_decode(pass, up->password, sizeof(up->password)-1); + if (n > 0) + { + up->password[n] = '\0'; + n = plugin_base64_decode(resp, up->response, sizeof(up->response)-1); + if (n > 0) + { + up->response[n] = '\0'; + if (DEBUG(up->verb)) + { + fprintf(stderr, "AUTH-PAM: BACKGROUND: parsed static challenge password\n"); + } + goto out; + } + } + + /* decode error: reinstate original value of up->password and return */ + plugin_secure_memzero(up->password, sizeof(up->password)); + plugin_secure_memzero(up->response, sizeof(up->response)); + strcpy(up->password, tmp); /* tmp is guaranteed to fit in up->password */ + + fprintf(stderr, "AUTH-PAM: base64 decode error while parsing static challenge password\n"); + +out: + if (tmp) + { + plugin_secure_memzero(tmp, strlen(tmp)); + free(tmp); + } +} + OPENVPN_EXPORT int openvpn_plugin_open_v3(const int v3structver, struct openvpn_plugin_args_open_in const *args, @@ -316,6 +380,7 @@ openvpn_plugin_open_v3(const int v3structver, /* Save global pointers to functions exported from openvpn */ plugin_secure_memzero = args->callbacks->plugin_secure_memzero; + plugin_base64_decode = args->callbacks->plugin_base64_decode; /* * Make sure we have two string arguments: the first is the .so name, @@ -599,6 +664,10 @@ my_conv(int n, const struct pam_message **msg_array, { aresp[i].resp = searchandreplace(match_value, "COMMONNAME", up->common_name); } + else if (strstr(match_value, "OTP")) + { + aresp[i].resp = searchandreplace(match_value, "OTP", up->response); + } else { aresp[i].resp = strdup(match_value); @@ -787,6 +856,9 @@ pam_server(int fd, const char *service, int verb, const struct name_value_list * #endif } + /* If password is of the form SCRV1:base64:base64 split it up */ + split_scrv1_password(&up); + if (pam_auth(service, &up)) /* Succeeded */ { if (send_control(fd, RESPONSE_VERIFY_SUCCEEDED) == -1) @@ -818,10 +890,11 @@ pam_server(int fd, const char *service, int verb, const struct name_value_list * command); goto done; } + plugin_secure_memzero(up.response, sizeof(up.response)); } done: - plugin_secure_memzero(up.password, sizeof(up.password)); + plugin_secure_memzero(up.response, sizeof(up.response)); #ifdef USE_PAM_DLOPEN dlclose_pam(); #endif