/* Editor Settings: expandtabs and use 4 spaces for indentation * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: * * -*- mode: c, c-basic-offset: 4 -*- */ /* * Copyright Likewise Software 2004-2008 * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. You should have received a copy of the GNU General * Public License along with this program. If not, see * . * * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING * TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU * GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT * license@likewisesoftware.com */ /* * Copyright (C) Likewise Software. All rights reserved. * * Module Name: * * main.c * * Abstract: * * Likewise IO (LWIO) * * Tool to copy files/directories * * Authors: Krishna Ganugapati (krishnag@likewisesoftware.com) * Sriram Nambakam (snambakam@likewisesoftware.com) */ #define _POSIX_PTHREAD_SEMANTICS 1 #include "includes.h" static NTSTATUS ParseArgs( int argc, char* argv[], PSTR* ppszCachePath, PSTR* ppszSourcePath, PSTR* ppszTargetPath, PSTR* ppszPrincipal, PSTR* ppszPassword, PBOOLEAN pbCopyRecursive ); static NTSTATUS LwIoReadPassword( PSTR* ppszPassword ); static NTSTATUS GetPassword( PSTR* ppszPassword ); static NTSTATUS LwIoCreateKrb5Cache( PCSTR pszPrincipal, PCSTR pszPassword, PSTR* ppszCachePath ); static VOID ShowUsage( VOID ); static NTSTATUS GetKrb5PrincipalName( PCSTR pszCachePath, PSTR* ppszPrincipalName ); static VOID MapErrorCodes( NTSTATUS ntStatus ); static VOID LwIoExitHandler( VOID ); static NTSTATUS LwIoDestroyKrb5Cache( PCSTR pszCachePath ); int main( int argc, char* argv[] ) { NTSTATUS ntStatus = STATUS_SUCCESS; PSTR pszSourcePath = NULL; PSTR pszTargetPath = NULL; PSTR pszCachePath = NULL; PSTR pszPrincipal = NULL; PSTR pszPassword = NULL; PIO_CREDS pCreds = NULL; BOOLEAN bRevertThreadCreds = FALSE; BOOLEAN bDestroyKrb5Cache = FALSE; BOOLEAN bCopyRecursive = FALSE; if (atexit(LwIoExitHandler) < 0) { ntStatus = LwErrnoToNtStatus(errno); BAIL_ON_NT_STATUS(ntStatus); } ntStatus = ParseArgs( argc, argv, &pszCachePath, &pszSourcePath, &pszTargetPath, &pszPrincipal, &pszPassword, &bCopyRecursive); BAIL_ON_NT_STATUS(ntStatus); if (!IsNullOrEmptyString(pszPrincipal)) { if (!pszPassword) { ntStatus = LwIoReadPassword(&pszPassword); BAIL_ON_NT_STATUS(ntStatus); } LWIO_SAFE_FREE_STRING(pszCachePath); ntStatus = LwIoCreateKrb5Cache( pszPrincipal, pszPassword, &pszCachePath); BAIL_ON_NT_STATUS(ntStatus); bDestroyKrb5Cache = TRUE; ntStatus = SMBAllocateString(pszCachePath, &gpszLwioCopyKrb5CachePath); BAIL_ON_NT_STATUS(ntStatus); bDestroyKrb5Cache = FALSE; } if (!IsNullOrEmptyString(pszCachePath)) { ntStatus = GetKrb5PrincipalName(pszCachePath, &pszPrincipal); if (IsNullOrEmptyString(pszPrincipal)) { fprintf(stderr,"Invalid cache path \n"); ntStatus = STATUS_INVALID_PARAMETER; BAIL_ON_NT_STATUS(ntStatus); } ntStatus = LwIoCreateKrb5CredsA( pszPrincipal, pszCachePath, &pCreds); BAIL_ON_NT_STATUS(ntStatus); ntStatus = LwIoSetThreadCreds(pCreds); BAIL_ON_NT_STATUS(ntStatus); bRevertThreadCreds = TRUE; } ntStatus = CopyFile(pszSourcePath, pszTargetPath, bCopyRecursive); BAIL_ON_NT_STATUS(ntStatus); cleanup: if (pCreds) { if (bRevertThreadCreds) { LwIoSetThreadCreds(NULL); } LwIoDeleteCreds(pCreds); } if (bDestroyKrb5Cache) { LwIoDestroyKrb5Cache(pszCachePath); } LWIO_SAFE_FREE_STRING(pszTargetPath); LWIO_SAFE_FREE_STRING(pszSourcePath); LWIO_SAFE_FREE_STRING(pszCachePath); LWIO_SAFE_FREE_STRING(pszPrincipal); LWIO_SAFE_FREE_STRING(pszPassword); if(ntStatus == STATUS_SUCCESS) fprintf(stdout,"copy succeed\n"); return (ntStatus); error: MapErrorCodes(ntStatus); goto cleanup; } static NTSTATUS ParseArgs( int argc, char* argv[], PSTR* ppszCachePath, PSTR* ppszSourcePath, PSTR* ppszTargetPath, PSTR* ppszPrincipal, PSTR* ppszPassword, PBOOLEAN pbCopyRecursive ) { NTSTATUS ntStatus = STATUS_SUCCESS; typedef enum { PARSE_MODE_OPEN = 0, PARSE_MODE_KRB5_CACHE_PATH, PARSE_MODE_UPN, PARSE_MODE_PASSWORD } ParseMode; typedef enum { LWIO_COPY_KRB5_NO_SPEC = 0, LWIO_COPY_KRB5_CACHE_SPECIFIED, LWIO_COPY_KRB5_UPN_SPECIFIED } LwioCopyKrb5Spec; int iArg = 1; ParseMode parseMode = PARSE_MODE_OPEN; PSTR pszSourcePath = NULL; PSTR pszTargetPath = NULL; PSTR pszCachePath = NULL; PSTR pszPrincipal = NULL; PSTR pszPassword = NULL; LwioCopyKrb5Spec krb5Spec = LWIO_COPY_KRB5_NO_SPEC; BOOLEAN bCopyRecursive = FALSE; for (iArg = 1; iArg < argc; iArg++) { PSTR pszArg = argv[iArg]; switch (parseMode) { case PARSE_MODE_OPEN: if (!strcasecmp(pszArg, "-h") || !strcasecmp(pszArg, "--help")) { ShowUsage(); exit(0); } else if (!strcasecmp(pszArg, "-r")) { bCopyRecursive = TRUE; } else if (!strcasecmp(pszArg, "-k")) { parseMode = PARSE_MODE_KRB5_CACHE_PATH; } else if (!strcasecmp(pszArg, "-u")) { parseMode = PARSE_MODE_UPN; } else if (!strcasecmp(pszArg, "-p")) { parseMode = PARSE_MODE_PASSWORD; } else if (IsNullOrEmptyString(pszSourcePath)) { ntStatus = SMBAllocateString( pszArg, &pszSourcePath); BAIL_ON_NT_STATUS(ntStatus); } else if (IsNullOrEmptyString(pszTargetPath)) { ntStatus = SMBAllocateString( pszArg, &pszTargetPath); BAIL_ON_NT_STATUS(ntStatus); } else { ShowUsage(); fprintf(stderr, "Error: Invalid option \n"); exit(1); } break; case PARSE_MODE_KRB5_CACHE_PATH: if (krb5Spec == LWIO_COPY_KRB5_UPN_SPECIFIED) { fprintf(stderr, "Attempt to specify both the kerberos cache path and user principal name\n"); ntStatus = STATUS_INVALID_PARAMETER; BAIL_ON_NT_STATUS(ntStatus); } LWIO_SAFE_FREE_STRING(pszCachePath); ntStatus = SMBAllocateString( pszArg, &pszCachePath); BAIL_ON_NT_STATUS(ntStatus); krb5Spec = LWIO_COPY_KRB5_CACHE_SPECIFIED; parseMode = PARSE_MODE_OPEN; break; case PARSE_MODE_UPN: if (krb5Spec == LWIO_COPY_KRB5_CACHE_SPECIFIED) { fprintf(stderr, "Attempt to specify both the kerberos cache path and user principal name\n"); ntStatus = STATUS_INVALID_PARAMETER; BAIL_ON_NT_STATUS(ntStatus); } LWIO_SAFE_FREE_STRING(pszPrincipal); ntStatus = SMBAllocateString( pszArg, &pszPrincipal); BAIL_ON_NT_STATUS(ntStatus); krb5Spec = LWIO_COPY_KRB5_UPN_SPECIFIED; parseMode = PARSE_MODE_OPEN; break; case PARSE_MODE_PASSWORD: LWIO_SAFE_FREE_STRING(pszPassword); ntStatus = SMBAllocateString( pszArg, &pszPassword); BAIL_ON_NT_STATUS(ntStatus); parseMode = PARSE_MODE_OPEN; break; default: ShowUsage(); exit(0); break; } } if ( argc == 1 || (!pszSourcePath && !pszTargetPath) ) { ShowUsage(); fprintf(stderr, "Error: Missing the options to be passed\n"); exit(1); } if(pszSourcePath) { PSTR pszStr = NULL; pszStr = strchr (pszSourcePath,'-'); if ( pszStr && !strcmp( pszSourcePath, pszStr) ) { fprintf(stderr, "Invalid option '%s' passed\n",pszSourcePath); fprintf(stdout, "Try 'lwio-copy --help' for more information\n"); pszStr = NULL; ntStatus = STATUS_INVALID_PARAMETER; BAIL_ON_NT_STATUS(ntStatus); } } if(pszTargetPath) { PSTR pszStr = NULL; pszStr = strchr (pszTargetPath,'-'); if ( pszStr && !strcmp( pszTargetPath, pszStr) ) { fprintf(stderr, "Invalid option '%s' passed\n",pszTargetPath); fprintf(stdout, "Try 'lwio-copy --help' for more information\n"); pszStr = NULL; ntStatus = STATUS_INVALID_PARAMETER; BAIL_ON_NT_STATUS(ntStatus); } } if(!pszSourcePath) { fprintf(stderr, "Missing the path to be passed\n"); ntStatus = STATUS_INVALID_PARAMETER; BAIL_ON_NT_STATUS(ntStatus); } if(!pszTargetPath) { fprintf(stderr, "Missing the path to be passed\n"); ntStatus = STATUS_INVALID_PARAMETER; BAIL_ON_NT_STATUS(ntStatus); } *ppszCachePath = pszCachePath; *ppszSourcePath = pszSourcePath; *ppszTargetPath = pszTargetPath; *ppszPrincipal = pszPrincipal; *ppszPassword = pszPassword; *pbCopyRecursive = bCopyRecursive; cleanup: return ntStatus; error: *ppszCachePath = NULL; *ppszSourcePath = NULL; *ppszTargetPath = NULL; *ppszPrincipal = NULL; *ppszPassword = NULL; *pbCopyRecursive = FALSE; LWIO_SAFE_FREE_STRING(pszTargetPath); LWIO_SAFE_FREE_STRING(pszSourcePath); LWIO_SAFE_FREE_STRING(pszCachePath); LWIO_SAFE_FREE_STRING(pszPrincipal); LWIO_SAFE_FREE_STRING(pszPassword); goto cleanup; } static NTSTATUS GetKrb5PrincipalName( PCSTR pszCachePath, PSTR* ppszPrincipalName ) { NTSTATUS ntStatus = STATUS_SUCCESS; krb5_error_code ret = 0; krb5_context ctx = NULL; krb5_ccache cc = NULL; krb5_principal pKrb5Principal = NULL; PSTR pszKrb5PrincipalName = NULL; PSTR pszPrincipalName = NULL; ret = krb5_init_context(&ctx); BAIL_ON_KRB_ERROR(ctx, ret); ret = krb5_cc_resolve(ctx, pszCachePath, &cc); BAIL_ON_KRB_ERROR(ctx, ret); ret = krb5_cc_get_principal(ctx, cc, &pKrb5Principal); BAIL_ON_KRB_ERROR(ctx, ret); ret = krb5_unparse_name(ctx, pKrb5Principal, &pszKrb5PrincipalName); BAIL_ON_KRB_ERROR(ctx, ret); ntStatus = SMBAllocateString(pszKrb5PrincipalName, &pszPrincipalName); BAIL_ON_NT_STATUS(ntStatus); *ppszPrincipalName = pszPrincipalName; cleanup: if (ctx) { if (pszKrb5PrincipalName) { krb5_free_unparsed_name(ctx, pszKrb5PrincipalName); } if (pKrb5Principal) { krb5_free_principal(ctx, pKrb5Principal); } if (cc) { krb5_cc_close(ctx, cc); } krb5_free_context(ctx); } return ntStatus; error: *ppszPrincipalName = NULL; LWIO_SAFE_FREE_STRING(pszPrincipalName); ntStatus = STATUS_INVALID_ACCOUNT_NAME; goto cleanup; } static NTSTATUS LwIoReadPassword( PSTR* ppszPassword ) { NTSTATUS ntStatus = STATUS_SUCCESS; PSTR pszPassword = NULL; fprintf(stdout, "Password: "); fflush(stdout); ntStatus = GetPassword(&pszPassword); BAIL_ON_NT_STATUS(ntStatus); fprintf(stdout, "\n"); *ppszPassword = pszPassword; cleanup: return ntStatus; error: *ppszPassword = NULL; LWIO_SAFE_FREE_STRING(pszPassword); goto cleanup; } static NTSTATUS LwIoCreateKrb5Cache( PCSTR pszPrincipal, PCSTR pszPassword, PSTR* ppszCachePath ) { NTSTATUS ntStatus = STATUS_SUCCESS; krb5_error_code ret = 0; krb5_context ctx = NULL; krb5_ccache cc = NULL; krb5_principal client = NULL; krb5_creds creds = {0}; krb5_get_init_creds_opt opts; PCSTR pszTempCachePath = NULL; PSTR pszCachePath = NULL; PSTR pszIter = NULL; pszIter = index(pszPrincipal, '@'); if (!pszIter) { fprintf(stderr, "Invalid user principal [%s]\n", pszPrincipal); goto error; } // upper case the realm while (pszIter && *pszIter) { *pszIter = toupper((int)*pszIter); pszIter++; } ret = krb5_init_context(&ctx); BAIL_ON_KRB_ERROR(ctx, ret); krb5_get_init_creds_opt_init(&opts); krb5_get_init_creds_opt_set_tkt_life(&opts, 12 * 60 * 60); krb5_get_init_creds_opt_set_forwardable(&opts, TRUE); /* Generates a new filed based credentials cache in /tmp. */ ret = krb5_cc_new_unique( ctx, "FILE", "hint", &cc); BAIL_ON_KRB_ERROR(ctx, ret); ret = krb5_parse_name(ctx, pszPrincipal, &client); BAIL_ON_KRB_ERROR(ctx, ret); ret = krb5_get_init_creds_password( ctx, &creds, client, (PSTR)pszPassword, NULL, NULL, 0, NULL, &opts); BAIL_ON_KRB_ERROR(ctx, ret); ret = krb5_cc_initialize(ctx, cc, client); BAIL_ON_KRB_ERROR(ctx, ret); ret = krb5_cc_store_cred(ctx, cc, &creds); BAIL_ON_KRB_ERROR(ctx, ret); pszTempCachePath = krb5_cc_get_name(ctx, cc); ntStatus = SMBAllocateString(pszTempCachePath, &pszCachePath); BAIL_ON_NT_STATUS(ntStatus); *ppszCachePath = pszCachePath; cleanup: if (ctx) { if (client) { krb5_free_principal(ctx, client); } krb5_free_cred_contents(ctx, &creds); if (cc != NULL) { krb5_cc_close(ctx, cc); } krb5_free_context(ctx); } return ntStatus; error: *ppszCachePath = NULL; LWIO_SAFE_FREE_STRING(pszCachePath); goto cleanup; } static NTSTATUS GetPassword( PSTR* ppszPassword ) { NTSTATUS ntStatus = STATUS_SUCCESS; CHAR szBuf[129]; DWORD idx = 0; struct termios old, new; CHAR ch; memset(szBuf, 0, sizeof(szBuf)); tcgetattr(0, &old); memcpy(&new, &old, sizeof(struct termios)); new.c_lflag &= ~(ECHO); tcsetattr(0, TCSANOW, &new); while ( (idx < 128) ) { if (read(0, &ch, 1)) { if (ch != '\n') { szBuf[idx++] = ch; } else { break; } } else { ntStatus = LwErrnoToNtStatus(errno); BAIL_ON_NT_STATUS(ntStatus); } } if (idx == 128) { ntStatus = LwErrnoToNtStatus(ENOBUFS); BAIL_ON_NT_STATUS(ntStatus); } if (idx > 0) { ntStatus = SMBAllocateString(szBuf, ppszPassword); BAIL_ON_NT_STATUS(ntStatus); } else { *ppszPassword = NULL; } cleanup: tcsetattr(0, TCSANOW, &old); return ntStatus; error: *ppszPassword = NULL; goto cleanup; } static VOID ShowUsage( VOID ) { // printf("Usage: lwio-copy [-h] [-r] [ -k | -u @REALM] \n"); printf("Usage: lwio-copy [-h] [ -k | -u user-id@REALM -p ] \n"); printf("\t-h Show help\n"); // printf("\t-r Recurse when copying a directory\n"); printf("\t-k kerberos cache path\n"); printf("Usage: lwio-copy -r //imgserver.abc.com/public/apple.jpg .\n"); } static VOID LwIoExitHandler( VOID ) { if (!IsNullOrEmptyString(gpszLwioCopyKrb5CachePath)) { LwIoDestroyKrb5Cache(gpszLwioCopyKrb5CachePath); LWIO_SAFE_FREE_STRING(gpszLwioCopyKrb5CachePath); } } static NTSTATUS LwIoDestroyKrb5Cache( PCSTR pszCachePath ) { // TODO: Should we use krb5 apis to delete this file? return SMBRemoveFile(gpszLwioCopyKrb5CachePath); } static VOID MapErrorCodes( NTSTATUS ntStatus ) { switch (ntStatus) { case STATUS_INVALID_PARAMETER: fprintf(stderr,"Error: Invalid parameter passed\n"); break; case STATUS_NO_SUCH_FILE: fprintf(stderr,"Error: Invalid local file/dir path passed\n"); break; case STATUS_FILE_IS_A_DIRECTORY: fprintf(stderr,"Error: Invalid directory path passed\n"); fprintf(stdout,"Please proivde a valid file name to copy a file\n"); break; case STATUS_BAD_NETWORK_NAME: fprintf(stderr,"Error: Invalid remote path passed\n"); fprintf(stdout,"Please check if a valid remote host name/valid directory is provided\n"); break; case STATUS_OBJECT_PATH_INVALID: case STATUS_OBJECT_PATH_NOT_FOUND: case STATUS_OBJECT_PATH_SYNTAX_BAD: case STATUS_OBJECT_NAME_INVALID: case STATUS_OBJECT_NAME_NOT_FOUND: fprintf(stderr,"Error: Invalid remote file path passed\n"); break; case STATUS_UNSUCCESSFUL: fprintf(stderr,"Error: lwio-copy unsuccessfull\n"); fprintf(stdout,"Please check if lwiod and lsassd running\n"); break; case STATUS_LOGON_FAILURE: case STATUS_NO_SUCH_USER: fprintf(stderr,"Error: Logon failed, please check the username/password\n"); break; case STATUS_NO_SUCH_DOMAIN: fprintf(stderr,"Error: Invalid domain\n"); fprintf(stdout,"Please check if machine is joined to a domain\n"); break; case STATUS_PASSWORD_EXPIRED: fprintf(stderr,"Error: password expired\n"); break; case STATUS_INVALID_ACCOUNT_NAME: fprintf(stderr,"Error: Invalid account name\n"); break; case STATUS_WRONG_PASSWORD: fprintf(stderr,"Error: password mismatch\n"); break; case STATUS_TIME_DIFFERENCE_AT_DC: fprintf(stderr,"Error: clock skew detected\n"); break; case STATUS_ACCESS_DENIED: fprintf(stderr,"Error: Access denied\n"); fprintf(stdout,"Try logging in as a domain user having an admin privilege\n"); break; } }