Advertisement
david62277

SMS2 Script v2

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