@@ -1093,50 +1093,34 @@ vg_read_file(FILE *fp, char ***result, int *rescount)
10931093}
10941094
10951095
1096+ #if !defined(_WIN32 )
10961097/*
1097- * Copy a secret (e.g. a private key) to the X11 clipboard using xclip,
1098- * and schedule it to be cleared after timeout_secs -- but only if the
1099- * clipboard still holds exactly what we put there, so we don't clobber
1100- * something else the user copied in the meantime. Modeled after the
1101- * clipboard handling in "pass" (passwordstore.org).
1098+ * Copy a secret to the X11 clipboard using xclip, and schedule it to be
1099+ * cleared after timeout_secs -- but only if the clipboard still holds
1100+ * exactly what we put there, so we don't clobber something else the user
1101+ * copied in the meantime. Modeled after the clipboard handling in "pass"
1102+ * (passwordstore.org).
11021103 *
1103- * Returns 1 if the secret was copied to the clipboard, 0 if no clipboard
1104- * tool was available (in which case the caller should fall back to
1105- * printing the secret).
1104+ * Returns 1 on success, 0 if xclip is unavailable or failed.
11061105 */
1107- int
1108- vg_clipboard_copy_secret (const char * secret , int timeout_secs )
1106+ static int
1107+ vg_clipboard_copy_xclip (const char * secret , int timeout_secs )
11091108{
1110- #if defined(_WIN32 )
1111- return 0 ;
1112- #else
11131109 FILE * fp ;
11141110 pid_t pid ;
11151111 char cmd [256 ];
11161112 int status ;
11171113
1118- if (system ("command -v xclip >/dev/null 2>&1" ) != 0 ) {
1119- fprintf (stderr ,
1120- "vanitygen: xclip not found in PATH, "
1121- "printing private key instead\n" );
1114+ if (system ("command -v xclip >/dev/null 2>&1" ) != 0 )
11221115 return 0 ;
1123- }
11241116
1125- fp = popen ("xclip -selection clipboard" , "w" );
1126- if (!fp ) {
1127- fprintf (stderr ,
1128- "vanitygen: could not run xclip (%s), "
1129- "printing private key instead\n" , strerror (errno ));
1117+ fp = popen ("xclip -selection clipboard 2>/dev/null" , "w" );
1118+ if (!fp )
11301119 return 0 ;
1131- }
11321120 fputs (secret , fp );
11331121 status = pclose (fp );
1134- if (status != 0 ) {
1135- fprintf (stderr ,
1136- "vanitygen: xclip exited with status %d, "
1137- "printing private key instead\n" , status );
1122+ if (status != 0 )
11381123 return 0 ;
1139- }
11401124
11411125 pid = fork ();
11421126 if (pid < 0 )
@@ -1167,5 +1151,103 @@ vg_clipboard_copy_secret(const char *secret, int timeout_secs)
11671151 /* Reap the intermediate child, which exits immediately */
11681152 waitpid (pid , NULL , 0 );
11691153 return 1 ;
1154+ }
1155+
1156+ /*
1157+ * Copy a secret to the terminal's clipboard using the OSC 52 escape
1158+ * sequence, understood by most modern terminal emulators (Windows
1159+ * Terminal, iTerm2, kitty, xterm, mintty, ...) without requiring an X11
1160+ * server -- this is what makes it work over a plain SSH session into a
1161+ * headless box. Scheduled clearing has no readback to compare against
1162+ * (OSC 52 has no reliable, portable query), so it unconditionally sends
1163+ * an empty payload after timeout_secs, which terminals interpret as
1164+ * "clear the clipboard".
1165+ *
1166+ * Returns 1 on success, 0 if no terminal is attached.
1167+ */
1168+ static int
1169+ vg_clipboard_copy_osc52 (const char * secret , int timeout_secs )
1170+ {
1171+ FILE * tty ;
1172+ char * b64 ;
1173+ int b64_len ;
1174+ pid_t pid ;
1175+
1176+ tty = fopen ("/dev/tty" , "w" );
1177+ if (!tty )
1178+ return 0 ;
1179+
1180+ b64_len = 4 * ((strlen (secret ) + 2 ) / 3 ) + 1 ;
1181+ b64 = malloc (b64_len );
1182+ if (!b64 ) {
1183+ fclose (tty );
1184+ return 0 ;
1185+ }
1186+ EVP_EncodeBlock ((unsigned char * )b64 ,
1187+ (const unsigned char * )secret , strlen (secret ));
1188+
1189+ fprintf (tty , "\x1b]52;c;%s\x07" , b64 );
1190+ fflush (tty );
1191+ fclose (tty );
1192+ free (b64 );
1193+
1194+ pid = fork ();
1195+ if (pid < 0 )
1196+ /* Copy succeeded even though we can't schedule the clear */
1197+ return 1 ;
1198+
1199+ if (pid == 0 ) {
1200+ /* Detach a grandchild so the parent doesn't have to wait */
1201+ if (fork () > 0 )
1202+ _exit (0 );
1203+ setsid ();
1204+ close (0 );
1205+ close (1 );
1206+ close (2 );
1207+
1208+ sleep (timeout_secs );
1209+ tty = fopen ("/dev/tty" , "w" );
1210+ if (tty ) {
1211+ fprintf (tty , "\x1b]52;c;\x07" );
1212+ fflush (tty );
1213+ fclose (tty );
1214+ }
1215+ _exit (0 );
1216+ }
1217+
1218+ /* Reap the intermediate child, which exits immediately */
1219+ waitpid (pid , NULL , 0 );
1220+ return 1 ;
1221+ }
1222+ #endif /* !defined(_WIN32) */
1223+
1224+ /*
1225+ * Copy a secret (e.g. a private key) to the clipboard, preferring the X11
1226+ * clipboard via xclip when a display is available, and falling back to
1227+ * the OSC 52 terminal escape sequence otherwise (e.g. over a plain SSH
1228+ * session with no X server).
1229+ *
1230+ * Returns VG_CLIPBOARD_XCLIP or VG_CLIPBOARD_OSC52 if the secret was
1231+ * copied to the clipboard, VG_CLIPBOARD_NONE if no clipboard mechanism
1232+ * was available (in which case the caller should fall back to printing
1233+ * the secret).
1234+ */
1235+ int
1236+ vg_clipboard_copy_secret (const char * secret , int timeout_secs )
1237+ {
1238+ #if defined(_WIN32 )
1239+ return VG_CLIPBOARD_NONE ;
1240+ #else
1241+ if (getenv ("DISPLAY" ) &&
1242+ vg_clipboard_copy_xclip (secret , timeout_secs ))
1243+ return VG_CLIPBOARD_XCLIP ;
1244+
1245+ if (vg_clipboard_copy_osc52 (secret , timeout_secs ))
1246+ return VG_CLIPBOARD_OSC52 ;
1247+
1248+ fprintf (stderr ,
1249+ "vanitygen: no clipboard available, "
1250+ "printing private key instead\n" );
1251+ return VG_CLIPBOARD_NONE ;
11701252#endif
11711253}
0 commit comments