Browse code

Support for username-only auth file.

Make OpenVPN read the username from the auth file
parameter of --auth-user-pass and prompt for a
password if it's not in the file.

Rationale: Prior to this change OpenVPN either
required both username and password present in the
auth file or prompted for both on the console.
Unlike passwords usernames usually don't change and
can therefore be "hardcoded" in the config.

Signed-off-by: Michal Ludvig <mludvig@logix.net.nz>

Reviewed and updated to current master.

Signed-off-by: Adriaan de Jong <dejong@fox-it.com>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <1444553060-15946-1-git-send-email-dejong@fox-it.com>
URL: http://article.gmane.org/gmane.network.openvpn.devel/10255

Michal Ludvig authored on 2015/10/11 17:44:20
Showing 3 changed files
... ...
@@ -3799,7 +3799,8 @@ over the client's routing table.
3799 3799
 .B \-\-auth\-user\-pass [up]
3800 3800
 Authenticate with server using username/password.
3801 3801
 .B up
3802
-is a file containing username/password on 2 lines (Note: OpenVPN
3802
+is a file containing username/password on 2 lines. If the
3803
+password line is missing, OpenVPN will prompt for one. (Note: OpenVPN
3803 3804
 will only read passwords from a file if it has been built
3804 3805
 with the \-\-enable\-password\-save configure option, or on Windows
3805 3806
 by defining ENABLE_PASSWORD_SAVE in win/settings.in).
... ...
@@ -1003,7 +1003,9 @@ get_user_pass_cr (struct user_pass *up,
1003 1003
 
1004 1004
   if (!up->defined)
1005 1005
     {
1006
-      const bool from_stdin = (!auth_file || !strcmp (auth_file, "stdin"));
1006
+      bool from_authfile = (auth_file && !streq (auth_file, "stdin"));
1007
+      bool username_from_stdin = !from_authfile;
1008
+      bool password_from_stdin = !from_authfile;
1007 1009
 
1008 1010
       if (flags & GET_USER_PASS_PREVIOUS_CREDS_FAILED)
1009 1011
 	msg (M_WARN, "Note: previous '%s' credentials failed", prefix);
... ...
@@ -1013,7 +1015,7 @@ get_user_pass_cr (struct user_pass *up,
1013 1013
        * Get username/password from management interface?
1014 1014
        */
1015 1015
       if (management
1016
-	  && ((auth_file && streq (auth_file, "management")) || (from_stdin && (flags & GET_USER_PASS_MANAGEMENT)))
1016
+	  && ((auth_file && streq (auth_file, "management")) || (!from_authfile && (flags & GET_USER_PASS_MANAGEMENT)))
1017 1017
 	  && management_query_user_pass_enabled (management))
1018 1018
 	{
1019 1019
 	  const char *sc = NULL;
... ...
@@ -1050,11 +1052,61 @@ get_user_pass_cr (struct user_pass *up,
1050 1050
 	  if (!strlen (up->password))
1051 1051
 	    strcpy (up->password, "ok");
1052 1052
 	}
1053
-	  
1053
+      else if (from_authfile)
1054
+        {
1055
+          /*
1056
+           * Try to get username/password from a file.
1057
+           */
1058
+          FILE *fp;
1059
+          char password_buf[USER_PASS_LEN] = { '\0' };
1060
+
1061
+          warn_if_group_others_accessible (auth_file);
1062
+
1063
+          fp = platform_fopen (auth_file, "r");
1064
+          if (!fp)
1065
+            msg (M_ERR, "Error opening '%s' auth file: %s", prefix, auth_file);
1066
+
1067
+          if ((flags & GET_USER_PASS_PASSWORD_ONLY) == 0)
1068
+            {
1069
+              /* Read username first */
1070
+               if (fgets (up->username, USER_PASS_LEN, fp) == NULL)
1071
+                 msg (M_FATAL, "Error reading username from %s authfile: %s",
1072
+                      prefix,
1073
+                      auth_file);
1074
+             }
1075
+          chomp (up->username);
1076
+
1077
+          if (fgets (password_buf, USER_PASS_LEN, fp) != NULL)
1078
+            {
1079
+#ifndef ENABLE_PASSWORD_SAVE
1080
+              /*
1081
+               * Unless ENABLE_PASSWORD_SAVE is defined, don't allow sensitive passwords
1082
+               * to be read from a file.
1083
+               */
1084
+              if (flags & GET_USER_PASS_SENSITIVE)
1085
+                msg (M_FATAL, "Sorry, '%s' password cannot be read from a file", prefix);
1086
+#endif
1087
+              chomp (password_buf);
1088
+            }
1089
+
1090
+          if (flags & GET_USER_PASS_PASSWORD_ONLY && !password_buf[0])
1091
+                msg (M_FATAL, "Error reading password from %s authfile: %s", prefix, auth_file);
1092
+
1093
+          if (password_buf[0])
1094
+            strncpy(up->password, password_buf, USER_PASS_LEN);
1095
+          else
1096
+            password_from_stdin = 1;
1097
+
1098
+          fclose (fp);
1099
+
1100
+          if (!(flags & GET_USER_PASS_PASSWORD_ONLY) && strlen (up->username) == 0)
1101
+            msg (M_FATAL, "ERROR: username from %s authfile '%s' is empty", prefix, auth_file);
1102
+        }
1103
+
1054 1104
       /*
1055 1105
        * Get username/password from standard input?
1056 1106
        */
1057
-      else if (from_stdin)
1107
+      if (username_from_stdin || password_from_stdin)
1058 1108
 	{
1059 1109
 #ifndef WIN32
1060 1110
 	  /* did we --daemon'ize before asking for passwords? */
... ...
@@ -1092,7 +1144,7 @@ get_user_pass_cr (struct user_pass *up,
1092 1092
 	      buf_printf (&user_prompt, "Enter %s Username:", prefix);
1093 1093
 	      buf_printf (&pass_prompt, "Enter %s Password:", prefix);
1094 1094
 
1095
-	      if (!(flags & GET_USER_PASS_PASSWORD_ONLY))
1095
+	      if (username_from_stdin && !(flags & GET_USER_PASS_PASSWORD_ONLY))
1096 1096
 		{
1097 1097
 		  if (!get_console_input (BSTR (&user_prompt), true, up->username, USER_PASS_LEN))
1098 1098
 		    msg (M_FATAL, "ERROR: could not read %s username from stdin", prefix);
... ...
@@ -1100,7 +1152,7 @@ get_user_pass_cr (struct user_pass *up,
1100 1100
 		    msg (M_FATAL, "ERROR: %s username is empty", prefix);
1101 1101
 		}
1102 1102
 
1103
-	      if (!get_console_input (BSTR (&pass_prompt), false, up->password, USER_PASS_LEN))
1103
+	      if (password_from_stdin && !get_console_input (BSTR (&pass_prompt), false, up->password, USER_PASS_LEN))
1104 1104
 		msg (M_FATAL, "ERROR: could not not read %s password from stdin", prefix);
1105 1105
 
1106 1106
 #ifdef ENABLE_CLIENT_CR
... ...
@@ -1126,52 +1178,6 @@ get_user_pass_cr (struct user_pass *up,
1126 1126
 #endif
1127 1127
 	    }
1128 1128
 	}
1129
-      else
1130
-	{
1131
-	  /*
1132
-	   * Get username/password from a file.
1133
-	   */
1134
-	  FILE *fp;
1135
-      
1136
-#ifndef ENABLE_PASSWORD_SAVE
1137
-	  /*
1138
-	   * Unless ENABLE_PASSWORD_SAVE is defined, don't allow sensitive passwords
1139
-	   * to be read from a file.
1140
-	   */
1141
-	  if (flags & GET_USER_PASS_SENSITIVE)
1142
-	    msg (M_FATAL, "Sorry, '%s' password cannot be read from a file", prefix);
1143
-#endif
1144
-
1145
-	  warn_if_group_others_accessible (auth_file);
1146
-
1147
-	  fp = platform_fopen (auth_file, "r");
1148
-	  if (!fp)
1149
-	    msg (M_ERR, "Error opening '%s' auth file: %s", prefix, auth_file);
1150
-
1151
-	  if (flags & GET_USER_PASS_PASSWORD_ONLY)
1152
-	    {
1153
-	      if (fgets (up->password, USER_PASS_LEN, fp) == NULL)
1154
-		msg (M_FATAL, "Error reading password from %s authfile: %s",
1155
-		     prefix,
1156
-		     auth_file);
1157
-	    }
1158
-	  else
1159
-	    {
1160
-	      if (fgets (up->username, USER_PASS_LEN, fp) == NULL
1161
-		  || fgets (up->password, USER_PASS_LEN, fp) == NULL)
1162
-		msg (M_FATAL, "Error reading username and password (must be on two consecutive lines) from %s authfile: %s",
1163
-		     prefix,
1164
-		     auth_file);
1165
-	    }
1166
-      
1167
-	  fclose (fp);
1168
-      
1169
-	  chomp (up->username);
1170
-	  chomp (up->password);
1171
-      
1172
-	  if (!(flags & GET_USER_PASS_PASSWORD_ONLY) && strlen (up->username) == 0)
1173
-	    msg (M_FATAL, "ERROR: username from %s authfile '%s' is empty", prefix, auth_file);
1174
-	}
1175 1129
 
1176 1130
       string_mod (up->username, CC_PRINT, CC_CRLF, 0);
1177 1131
       string_mod (up->password, CC_PRINT, CC_CRLF, 0);
... ...
@@ -479,8 +479,10 @@ static const char usage_message[] =
479 479
   "Client options (when connecting to a multi-client server):\n"
480 480
   "--client         : Helper option to easily configure client mode.\n"
481 481
   "--auth-user-pass [up] : Authenticate with server using username/password.\n"
482
-  "                  up is a file containing username/password on 2 lines,\n"
483
-  "                  or omit to prompt from console.\n"
482
+  "                  up is a file containing the username on the first line,\n"
483
+  "                  and a password on the second. If either the password or both\n"
484
+  "                  the username and the password are omitted OpenVPN will prompt\n"
485
+  "                  for them from console.\n"
484 486
   "--pull           : Accept certain config file options from the peer as if they\n"
485 487
   "                  were part of the local config file.  Must be specified\n"
486 488
   "                  when connecting to a '--mode server' remote host.\n"