Advertisement
jobshopr

SMS2 Script v2.1

Mar 9th, 2017
1,673
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <#
  2. Written by David Ott
  3. Modified by Ed Hammond 3.9.2017 - http://edhammond.blogspot.com/
  4. Ed Hammond v2.1 Change log:
  5. -Added more details and graphics to the email
  6. -Added a better description inside the QR Code so that your Company and Email address show up in Google Authenticator. (line 237)
  7. -When the Powershell writes back to the database on line 232 I added Feitian Serial and the 30 second time out to that line. Feitian Serial seems to be required and not Google Authenticator for some reason.
  8.  
  9. This script is for mass deployment of SMS2 TOTP codes (version 2!) for use with Google/Azure Authenticator
  10. *****You need to run this with an account that has modify rights to the SMS2 database!*****
  11. Read through the script and note any variables you need to change for your environment - I have
  12. marked them with comments ##### <-- just search for that
  13.  
  14. Take special care to read anything marked with "*****"
  15.  
  16. I wrote it because as of right now there is no tool for SMS2 to deploy multiple TOTP keys at once (you
  17. have to do them one at a time)
  18.  
  19. ***** you will need qrcode.exe from https://code.google.com/p/qrencode-win32/wiki/Downloads for generating QR barcodes *****
  20.  
  21. I have to give credit to this page https://gist.github.com/JonFriesen/234c7471c3e3199f97d5 for
  22. the conversion of the base32 secret to hex (which is used in the database)
  23. Also, this page http://www.sharepointfire.com/MyBlog/2011/12/send-email-with-images-in-powershell/ for
  24. helping me edit my sendmail function to imbed an image
  25.  
  26. I in no way shape or form provide technical support for this script.  Do not use it in production until you have
  27. tested it in a development environment!
  28. #>
  29.  
  30. #requires -version 4
  31. <#####***** This version requires the Active Directory module! *****#####>
  32. try {
  33. ipmo activedirectory
  34. } catch {
  35. write-host "Requires Active Directory module" -f Red
  36. break
  37. }
  38. function Convert-Base32ToHex($base32) {
  39.     $base32chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
  40.     $bits = "";
  41.     $hex = "";
  42.  
  43.     for ($i = 0; $i -lt $base32.Length; $i++) {
  44.         $val = $base32chars.IndexOf($base32.Chars($i));
  45.         $binary = [Convert]::ToString($val, 2)
  46.         $staticLen = 5
  47.         $padder = '0'
  48.         $bits += Add-LeftPad $binary.ToString()  $staticLen  $padder
  49.     }
  50.  
  51.  
  52.     for ($i = 0; $i+4 -le $bits.Length; $i+=4) {
  53.         $chunk = $bits.Substring($i, 4)
  54.         $intChunk = [Convert]::ToInt32($chunk, 2)
  55.         $hexChunk = Convert-IntToHex($intChunk)
  56.         $hex = $hex + $hexChunk
  57.     }
  58.     return $hex;
  59.  
  60. }
  61.  
  62. function Convert-IntToHex([int]$num) {
  63.     return ('{0:x}' -f $num)
  64. }
  65.  
  66. function Add-LeftPad($str, $len, $pad) {
  67.     if(($len + 1) -ge $str.Length) {
  68.         while (($len - 1) -ge $str.Length) {
  69.             $str = ($pad + $str)
  70.         }
  71.     }
  72.     return $str;
  73. }
  74. function sendMail{
  75. <##### Change the smtp server to yours #####>    
  76.      $smtpServer = "yoursmtp.company.com"
  77.      $msg = new-object Net.Mail.MailMessage
  78.      $att1 = new-object Net.Mail.Attachment($img)
  79.      $att1.ContentType.MediaType = “image/png”
  80.      $att1.ContentId = “Attachment”
  81.      $msg.Priority = [System.Net.Mail.Mailpriority]::High
  82.      $msg.IsBodyHtml = $true
  83.      $msg.Attachments.add($att1)
  84.      $smtp = new-object Net.Mail.SmtpClient($smtpServer)
  85. <##### set  your from address #####>
  86.      $msg.From = "Notification@yourcompany.com"
  87.      $msg.To.Add($address)
  88.      $msg.subject = "Two Factor Authentication"
  89.      $msg.body = $message
  90.      $smtp.Send($msg)  
  91. }
  92. <##### csv file to store information - just make sure the folder exists #####>
  93. $file = "\\server\share\logs\totplog.csv"
  94. <##### Directory to store the QR .png files #####>
  95. $qrdir = "\\server\share\QR\"
  96. <##### Place qrcode.exe (link above) in the same directory where you are putting the QR .png files
  97. you can change this if you want but you have to update the path to qrcode.exe below #####>
  98. $qrcode = Join-Path $qrdir "qrcode.exe"
  99. if (!(test-path $file)) {
  100.  
  101. $oldlog = @()
  102. } else {
  103. $oldlog = Import-Csv $file
  104. }
  105.  
  106. <##### Edit the $sqlserver variable to equal your sql server name and if the database is not SMS_DB
  107. update the $sqldbname variable - remember you must run this script as an AD user who has rights to edit
  108. the database!! #####>
  109. $SQLServer = "SQLServerName"
  110. $SQLDBName = "SMS_DB"
  111. $SqlQuery = "select * from SMS_Contact"
  112. $SqlQuery1 = "select * from UserProviders"
  113. $SqlConnection = New-Object System.Data.SqlClient.SqlConnection
  114. $SqlConnection.ConnectionString = "Server = $SQLServer; Database = $SQLDBName; Integrated Security = True; MultipleActiveResultSets = True"
  115. $SqlCmd = New-Object System.Data.SqlClient.SqlCommand
  116. $SqlCmd.CommandText = $SqlQuery
  117. $SqlCmd.Connection = $SqlConnection
  118. $SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
  119. $SqlAdapter.SelectCommand = $SqlCmd
  120. $DataSet = New-Object System.Data.DataSet
  121. $SqlAdapter.Fill($DataSet)
  122.  
  123. $SqlCmd1 = New-Object System.Data.SqlClient.SqlCommand
  124. $SqlCmd1.CommandText = $SqlQuery1
  125. $SqlCmd1.Connection = $SqlConnection
  126. $SqlAdapter1 = New-Object System.Data.SqlClient.SqlDataAdapter
  127. $SqlAdapter1.SelectCommand = $SqlCmd1
  128. $DataSet1 = New-Object System.Data.DataSet
  129. $SqlAdapter1.Fill($DataSet1)
  130. [array]$users = $dataset.tables[0] | ?{$_.userstatus -eq "1"}
  131. [array]$codes = $dataset1.tables[0]
  132. <#####***** Here is where this script starts to really differ from the first one I created.  This next bit will prompt you
  133. for the SAMACCOUNTNAME of an AD group.  If the group does not exist, or is invalid it will start gathering information on
  134. ALL users in AD.  This can take some time to process depending on how many users are in the group/domain.  It has to compare
  135. 2 tables in the database for each user to see if there is a TOTP value or not - in the end lets you know which users are
  136. already configured in the out-gridview *****#####>
  137. $gp = Read-Host "Enter the SamAccountName of the AD Group you wish to focus on`r`nIf you do not supply a any information, or if the SamAccountName`r`nis invalid you will be returned all users"
  138. $gpt = "0"
  139. try {
  140. $testgp = Get-ADGroup $gp
  141. $gpt = "1"
  142. } catch {
  143. $gpt = "0"
  144. }
  145. $users1 = $null
  146. if ($gpt -eq "0") {
  147. <#####***** If the group SAMACCOUNTNAME is invalid or not supplied this will get a count of all users in the database.  
  148. I filter out some based on name.  "Radiusu" is the user I use from the netscaler to monitor SMS2, so if you have something
  149. like that you should filter that user out.  I also filter out some default accounts, and computer accounts *****#####>
  150. Write-Host "AD group name invalid, or not entered.  Gathering information on ALL users.`r`nThis will take a long time... get some coffee" -f Red
  151. $z = 0
  152. <#####***** Gets the count of all users in the database (you may need to edit the filters for your environment), so the progress bar
  153. will work *****#####>
  154. $adc = ($users | ?{$_.ad_username -ne "radiusu"` #####***** Radiusu is a user I use to monitor SMS2 from the Netscaler
  155.  -and $_.ad_username -notlike "*$*"`
  156.  -and $_.ad_username -notlike "sm_*"`
  157.   -and $_.ad_username -notlike "cntd_*"`
  158.    -and $_.ad_username -notlike "IUSR_*"`
  159.    -and $_.ad_username -notlike "IWAM_*"`
  160.    -and $_.ad_username -notlike "ILS_*"}).count
  161. <#####***** Gets all the users in the database (again you may need to edit the filters for your environment).  Then for each
  162. one checks to see if they already have a totp code setup (that can take a while depending on how many users it has to process).
  163. Once it has gathered all the information it will spit it out in a grid and you can select which users you want to configure.
  164.  *****#####>
  165. $users1 = $users | ?{$_.ad_username -ne "radiusu"` #####***** Radiusu is a user I use to monitor SMS2 from the Netscaler
  166.  -and $_.ad_username -notlike "*$*"`
  167.  -and $_.ad_username -notlike "sm_*"`
  168.   -and $_.ad_username -notlike "cntd_*"`
  169.    -and $_.ad_username -notlike "IUSR_*"`
  170.    -and $_.ad_username -notlike "IWAM_*"`
  171.    -and $_.ad_username -notlike "ILS_*"} | select id,ad_username,display_name,email | %{
  172.    $z++
  173. Write-Progress -Activity "Gathering Users" -Status "User $z of $adc" -PercentComplete ($z/$adc*100)
  174.    $bid = $null
  175. $bid = $_.id
  176. $_ | select id,ad_username,display_name,email,@{name="Configured";expression={if (($codes | ?{$_.userid -eq $bid -and $_.authproviderid -eq "2"} | select -expand config) -like "totp*"){$true}else{$false} }}
  177. } | sort configured | Out-GridView -PassThru -Title "Users"
  178.    } else {
  179. <#####***** If the SAMACCOUNTNAME of the group is valid the script grabs every user object in the group recursively (nested
  180. groups).  Then for each user - if the user is enabled in AD - it will process like above. *****#####>
  181.    $adus = $null
  182.    $adus = Get-ADGroupMember $gp -Recursive | ?{$_.objectclass -eq "user"} | select -expand samaccountname | %{
  183. if ((Get-ADUser $_).enabled -eq $true) {
  184. $_
  185. }
  186. } | sort -unique
  187. Write-Host "Group $gp is valid.  Please wait as information is gathered on" $adus.count "users." -f Green
  188. $z = 0
  189. $adc = $adus.count
  190. $users1 = $users | ?{$adus -eq $_.ad_username} | select id,ad_username,display_name,email | %{
  191. $z++
  192. Write-Progress -Activity "Gathering Users" -Status "User $z of $adc" -PercentComplete ($z/$adc*100)
  193. $bid = $null
  194. $bid = $_.id
  195. $_ | select id,ad_username,display_name,email,@{name="Configured";expression={if (($codes | ?{$_.userid -eq $bid -and $_.authproviderid -eq "2"} | select -expand config) -like "totp*"){$true}else{$false} }}
  196. } | sort configured | Out-GridView -PassThru -Title "Users"
  197.    }
  198. <#####***** If you don't select any users in the gridview this will end the script *****#####>
  199. if (($users1 | Measure-Object).count -lt "1") {break}
  200. <#####***** Here is where the magic starts to happen!  For each user you selected it will create a random totp string, convert
  201. it to hex (which is used in the database), update the .csv log file, write the totp information to the database, create the
  202. QR .png file, email the information to the user (if they do not appear to have an email address it will display the
  203. information in the PS window to send the user on the screen), and finally remove the user from the exclusion group (see
  204. the very end of the script for more info). *****#####>
  205. $arrayall = @()
  206. $arrayall += $oldlog
  207. $SqlConnection.open()
  208. foreach ($user in $users1) {
  209. $totp = (([char[]](Get-Random -Input $(50..55 + 65..90 + 97..122) -Count 16)) -join "").toupper()
  210. $hex = (Convert-Base32ToHex -base32 $totp).toupper()
  211. $id = $null
  212. $un = $null
  213. $em = $null
  214.  
  215. [string]$id = $user.ID
  216. [string]$un = ($user.AD_USERNAME).toupper()
  217. [string]$em = ($user.email).tolower()
  218. if ($arrayall | ?{$_.username -eq "$un"}) {
  219. $userupdate = $null
  220. $userupdate = ($arrayall | ?{$_.username -eq "$un"})
  221. $userupdate.totp = $totp
  222. $userupdate.hex = $hex
  223. } else {
  224. $update = $null
  225. $update = New-Object psobject
  226. $update | Add-Member -type NoteProperty -name 'id' -Value $id
  227. $update | Add-Member -type NoteProperty -name 'username' -Value $un
  228. $update | Add-Member -type NoteProperty -name 'email' -Value $em
  229. $update | Add-Member -type NoteProperty -name 'totp' -Value $totp
  230. $update | Add-Member -type NoteProperty -name 'hex' -Value $hex
  231. $arrayall += $update
  232. }
  233.  
  234. $arrayall | Export-Csv $file
  235. $SqlCmd2 = $null
  236. $SqlCmd2 = New-Object System.Data.SqlClient.SqlCommand
  237. $sqlcmd2.commandtext = "update userProviders
  238. set config = 'TOTP,$hex,0,30,Default,Feitian Serial'
  239. where userId = '$id' AND authProviderId = '2'"
  240. $sqlcmd2.Connection = $SqlConnection
  241. $sqlcmd2.ExecuteReader()
  242. <#####***** You can replace the "Work-VPN" with your company on the end of the next line. If you add spaces you will need to escape or quote them correctly *****#####>
  243. Invoke-Expression -Command "$qrcode -o $qrdir\$un.png -s 3 -l H otpauth://totp/$em`?secret=$totp""&""issuer=Work-VPN"
  244. if ($em -notlike "*@*") {
  245. Write-Host "User $un does not have an email address associated with their account!" -f Red
  246. Write-Host "You will need to give this user their secret key `"$totp`" and/or QR code`r`n $qrdir\$un.png`r`n"
  247. } else {
  248. $address = $em
  249. $img = "$qrdir\$un.png"
  250. <#####***** Below is the message in HTML format that your users will be sent to your users.  I suggest setting up a test
  251. URL for your users (you will need to edit the message to fit your environment). Also, if you have a helpdesk email address edit
  252. that line as well.  In order for the QR code and totp string to show up in the email make sure you leave the
  253. <img src="cid:Attachment"><br>
  254. $totp
  255. lines alone.
  256.  *****#####>
  257. $message = @"
  258. <html>
  259. <body>
  260. <style>
  261.    body {
  262.        background-color: White;
  263.    }
  264.    p,ul,li {
  265.        font-family: Arial, sans-serif;
  266.        font-size: 14px;
  267.    }
  268. </style>
  269. <img src="###http://mycompanyhomepage.com/logo.png###"><br>
  270. <p>We are adding a Two factor Authentication requirement for VPN access.
  271. <br>You will need to install Google Authenticator from the app store on your phone and then scan the QR code with Google Authenticator app.
  272. <br>Your Secure QR code and secret key can be found below.  
  273. <br>Please use either the code or the secret key to setup the Google Authenticator token on your smartphone, and then delete this email.
  274. <br>Be aware that it is crucial to keep this information safe from hackers by deleting this email when your done.<br>
  275. <img src="cid:Attachment"><br>
  276. $totp
  277. </p>
  278. <p>When you connect to the VPN, you will login as usual and then a second window will ask you to enter the code from Google Authenicator.<br><br><br>
  279. If authentication fails, please contact the <a href="mailto:helpdesk@yourcompany.com?subject=Two Factor Authentication Help">Help Desk</a> for assistance.
  280. <br><br>
  281. <a href="https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8">Get Google Authenticator for your iPhone</a><br><br>
  282. <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en">Get Google Authenticator for your Android Phone</a><br><br>
  283. <img src="http://a2.mzstatic.com/us/r30/Purple49/v4/a8/3d/8c/a83d8cde-dd69-de15-3561-a118d59b4be4/screen696x696.jpeg">
  284. </body>
  285. </html>
  286. "@
  287. Write-Host "Sending email to $un"
  288. sendMail
  289. }
  290. <#####***** In my environment I have a 2 factor exclusion group.  If you are in it you don't have to supply a token code when
  291. logging in (or can put in anything in that box).  This will remove the user from that group.  If you have something similar
  292. edit the group name below.  If not then remove or comment out the next 5 lines. *****#####>
  293. try {
  294. Remove-ADGroupMember 2factorexclude $un -Confirm:$false
  295. } catch {
  296. $null
  297. }
  298. }
  299. $SqlConnection.close()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement